#!/usr/bin/env python
# -*- coding: utf-8 -*-

# This file is part of Beremiz, a Integrated Development Environment for
# programming IEC 61131-3 automates supporting plcopen standard and CanFestival.
#
# Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
#
# See COPYING file for copyrights details.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

from __future__ import absolute_import
from __future__ import division

import base64
import locale
import os
import pickle
import sys
import traceback
from os.path import exists

import wx
import wx.aui
from loguru import logger
from wx import MDIParentFrame
from wxasync import AsyncBind

from resources import GetBitmap
from .PLCControler import PLCControler, reversed_word
from .controls.CustomTree import CustomTree
from .controls.DebugVariablePanel.DebugVariablePanel import DebugVariablePanel
from .controls.LibraryPanel import LibraryPanel
from .controls.PouInstanceVariablesPanel import PouInstanceVariablesPanel
from .controls.SearchResultPanel import SearchResultPanel
from .dialogs import ProjectDialog, PouTransitionDialog, PouActionDialog
from .dialogs.FindInPouDialog import FindInPouDialog
from .dialogs.PouDialog import PouDialog
from .dialogs.SearchInProjectDialog import SearchInProjectDialog
from .editors.DataTypeEditor import DataTypeEditor
from .editors.EditorPanel import EditorPanel
from .editors.LDViewer import LD_Viewer
from .editors.ResourceEditor import ConfigurationEditor, ResourceEditor
from .editors.SFCViewer import SFC_Viewer
from .editors.TextViewer import TextViewer
from .editors.Viewer import Viewer, ZOOM_FACTORS
from .graphics.GraphicCommons import FREEDRAWING_MODE, DRIVENDRAWING_MODE, MODE_SELECTION, MODE_MOTION, MODE_COMMENT, \
    MODE_VARIABLE, MODE_BLOCK, MODE_CONNECTION, MODE_POWERRAIL, MODE_COIL, MODE_CONTACT, MODE_INITIALSTEP, MODE_STEP, \
    MODE_ACTION, MODE_TRANSITION, MODE_DIVERGENCE, ERROR_HIGHLIGHT, SEARCH_RESULT_HIGHLIGHT, MODE_JUMP, MiterPen
from .plcopen.definitions import LANGUAGES
from .plcopen.structures import IL_KEYWORDS, ST_KEYWORDS, TestIdentifier, IEC_KEYWORDS
from .plcopen.types_enums import UNEDITABLE_NAMES, ITEM_PROJECT, ITEM_TRANSITION, ITEM_ACTION, ITEM_CONFIGURATION, \
    ITEM_RESOURCE, ITEM_DATATYPE, ITEM_DATATYPES, ITEM_FUNCTION, ITEM_FUNCTIONBLOCK, ITEM_PROGRAM, ITEM_VAR_LOCAL, \
    ITEM_VAR_GLOBAL, ITEM_VAR_EXTERNAL, ITEM_VAR_TEMP, ITEM_VAR_INPUT, ITEM_VAR_OUTPUT, ITEM_VAR_INOUT, \
    ITEM_TRANSITIONS, ITEM_ACTIONS, ITEM_CONFIGURATIONS, ITEM_RESOURCES, ITEM_PROPERTIES, GetElementType, ITEM_POU, \
    ITEMS_UNEDITABLE, ComputeDataTypeName, ComputePouName, ComputePouTransitionName, ComputePouActionName, \
    ComputeConfigurationName, ComputeConfigurationResourceName, ITEMS_VARIABLE
from .util.ProcessLogger import ProcessLogger

_ = wx.GetTranslation
if sys.platform == 'win32':
    USETAB = None
else:
    USETAB = True
# Define PLCOpenEditor controls id

[
    ID_PLCOPENEDITOR, ID_PLCOPENEDITORLEFTNOTEBOOK,
    ID_PLCOPENEDITORBOTTOMNOTEBOOK, ID_PLCOPENEDITORRIGHTNOTEBOOK,
    ID_PLCOPENEDITORPROJECTTREE, ID_PLCOPENEDITORMAINSPLITTER,
    ID_PLCOPENEDITORSECONDSPLITTER, ID_PLCOPENEDITORTHIRDSPLITTER,
    ID_PLCOPENEDITORLIBRARYPANEL, ID_PLCOPENEDITORLIBRARYSEARCHCTRL,
    ID_PLCOPENEDITORLIBRARYTREE, ID_PLCOPENEDITORLIBRARYCOMMENT,
    ID_PLCOPENEDITORTABSOPENED, ID_PLCOPENEDITORTABSOPENED1,
    ID_PLCOPENEDITOREDITORMENUTOOLBAR, ID_PLCOPENEDITOREDITORTOOLBAR,
    ID_PLCOPENEDITORPROJECTPANEL,
] = [wx.NewIdRef() for _init_ctrls in range(17)]

# Define PLCOpenEditor EditMenu extra items id
[
    ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO, ID_PLCOPENEDITOREDITMENUADDDATATYPE,
    ID_PLCOPENEDITOREDITMENUADDFUNCTION, ID_PLCOPENEDITOREDITMENUADDFUNCTIONBLOCK,
    ID_PLCOPENEDITOREDITMENUADDPROGRAM, ID_PLCOPENEDITOREDITMENUADDCONFIGURATION,
    ID_PLCOPENEDITOREDITMENUFINDNEXT, ID_PLCOPENEDITOREDITMENUFINDPREVIOUS,
    ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, ID_PLCOPENEDITOREDITMENUADDRESOURCE
] = [wx.NewIdRef() for _init_coll_EditMenu_Items in range(10)]

# Define PLCOpenEditor DisplayMenu extra items id
[
    ID_PLCOPENEDITORDISPLAYMENURESETPERSPECTIVE,
    ID_PLCOPENEDITORDISPLAYMENUSWITCHPERSPECTIVE,
    ID_PLCOPENEDITORDISPLAYMENUFULLSCREEN,
] = [wx.NewIdRef() for _init_coll_DisplayMenu_Items in range(3)]

# -------------------------------------------------------------------------------
#                            EditorToolBar definitions
# -------------------------------------------------------------------------------

# Define PLCOpenEditor Toolbar items id
[
    ID_PLCOPENEDITOREDITORTOOLBARSELECTION, ID_PLCOPENEDITOREDITORTOOLBARCOMMENT,
    ID_PLCOPENEDITOREDITORTOOLBARVARIABLE, ID_PLCOPENEDITOREDITORTOOLBARBLOCK,
    ID_PLCOPENEDITOREDITORTOOLBARCONNECTION, ID_PLCOPENEDITOREDITORTOOLBARWIRE,
    ID_PLCOPENEDITOREDITORTOOLBARPOWERRAIL, ID_PLCOPENEDITOREDITORTOOLBARRUNG,
    ID_PLCOPENEDITOREDITORTOOLBARCOIL, ID_PLCOPENEDITOREDITORTOOLBARICOIL, ID_PLCOPENEDITOREDITORTOOLBARCONTACT,
    ID_PLCOPENEDITOREDITORTOOLBARBRANCH, ID_PLCOPENEDITOREDITORTOOLBARINITIALSTEP,
    ID_PLCOPENEDITOREDITORTOOLBARSTEP, ID_PLCOPENEDITOREDITORTOOLBARTRANSITION,
    ID_PLCOPENEDITOREDITORTOOLBARACTIONBLOCK, ID_PLCOPENEDITOREDITORTOOLBARDIVERGENCE,
    ID_PLCOPENEDITOREDITORTOOLBARJUMP, ID_PLCOPENEDITOREDITORTOOLBARMOTION,
] = [wx.NewIdRef() for _init_coll_DefaultEditorToolBar_Items in range(19)]


# -------------------------------------------------------------------------------
#                               Helper Functions
# -------------------------------------------------------------------------------


def EncodeFileSystemPath(path, use_base64=True):
    path = path.encode(sys.getfilesystemencoding())
    if use_base64:
        return base64.encodebytes(path)
    return path


def DecodeFileSystemPath(path, is_base64=True):
    if is_base64:
        path = base64.decodebytes(path)
    if not isinstance(path, str):
        path = path.decode()
    return path


def AppendMenu(parent, help, id, kind, text):
    return parent.Append(helpString=help, id=id, kind=kind, item=text)


[
    TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, PROJECTTREE,
    POUINSTANCEVARIABLESPANEL, LIBRARYTREE, SCALING, PAGETITLES
] = range(10)


def GetShortcutKeyCallbackFunction(viewer_function):
    def ShortcutKeyFunction(self, event):
        control = self.FindFocus()
        if control is not None and control.GetName() in ["Viewer", "TextViewer"]:
            getattr(control.ParentWindow, viewer_function)()
        elif isinstance(control, wx.stc.StyledTextCtrl):
            getattr(control, viewer_function)()
        elif isinstance(control, wx.TextCtrl):
            control.ProcessEvent(event)

    return ShortcutKeyFunction


def GetDeleteElementFunction(remove_function, parent_type=None, check_function=None):
    def DeleteElementFunction(self, selected):
        name = self.ProjectTree.GetItemText(selected)
        if check_function is None or check_function(name):
            if parent_type is not None:
                item_infos = self.ProjectTree.GetItemData(selected)
                parent_name = item_infos["tagname"].split("::")[1]
                remove_function(self.Controler, parent_name, name)
            else:
                remove_function(self.Controler, name)

    return DeleteElementFunction


if wx.Platform == '__WXMSW__':
    TAB_BORDER = 6
    NOTEBOOK_BORDER = 6
else:
    TAB_BORDER = 7
    NOTEBOOK_BORDER = 2


def SimplifyTabLayout(tabs, rect):
    for tab in tabs:
        if tab["pos"][0] == rect.x:
            others = [t for t in tabs if t != tab]
            others.sort(key=lambda x: x["pos"][0])
            for other in others:
                if other["pos"][1] == tab["pos"][1] and \
                        other["size"][1] == tab["size"][1] and \
                        other["pos"][0] == tab["pos"][0] + tab["size"][0] + TAB_BORDER:

                    tab["size"] = (tab["size"][0] + other["size"][0] + TAB_BORDER, tab["size"][1])
                    tab["pages"].extend(other["pages"])
                    tabs.remove(other)

                    if tab["size"][0] == rect.width:
                        return True

        elif tab["pos"][1] == rect.y:
            others = [t for t in tabs if t != tab]
            others.sort(key=lambda x: x["pos"][1])
            for other in others:
                if other["pos"][0] == tab["pos"][0] and \
                        other["size"][0] == tab["size"][0] and \
                        other["pos"][1] == tab["pos"][1] + tab["size"][1] + TAB_BORDER:

                    tab["size"] = (tab["size"][0], tab["size"][1] + other["size"][1] + TAB_BORDER)
                    tab["pages"].extend(other["pages"])
                    tabs.remove(other)

                    if tab["size"][1] == rect.height:
                        return True
    return False


def ComputeTabsLayout(tabs, rect):
    if len(tabs) == 0:
        return tabs
    if len(tabs) == 1:
        return tabs[0]
    split = None
    split_id = None
    for idx, tab in enumerate(tabs):
        if len(tab["pages"]) == 0:
            raise ValueError("Not possible")
        if tab["size"][0] == rect.width:
            if tab["pos"][1] == rect.y:
                split = (wx.TOP, tab["size"][1] / rect.height)
                split_rect = wx.Rect(rect.x, rect.y + tab["size"][1] + TAB_BORDER,
                                     rect.width, rect.height - tab["size"][1] - TAB_BORDER)
            elif tab["pos"][1] == rect.height + 1 - tab["size"][1]:
                split = (wx.BOTTOM, 1.0 - tab["size"][1] / rect.height)
                split_rect = wx.Rect(rect.x, rect.y,
                                     rect.width, rect.height - tab["size"][1] - TAB_BORDER)
            split_id = idx
            break
        elif tab["size"][1] == rect.height:
            if tab["pos"][0] == rect.x:
                split = (wx.LEFT, tab["size"][0] / rect.width)
                split_rect = wx.Rect(rect.x + tab["size"][0] + TAB_BORDER, rect.y,
                                     rect.width - tab["size"][0] - TAB_BORDER, rect.height)
            elif tab["pos"][0] == rect.width + 1 - tab["size"][0]:
                split = (wx.RIGHT, 1.0 - tab["size"][0] / rect.width)
                split_rect = wx.Rect(rect.x, rect.y,
                                     rect.width - tab["size"][0] - TAB_BORDER, rect.height)
            split_id = idx
            break
    if split is not None:
        split_tab = tabs.pop(split_id)
        return {"split": split,
                "tab": split_tab,
                "others": ComputeTabsLayout(tabs, split_rect)}
    else:
        if SimplifyTabLayout(tabs, rect):
            return ComputeTabsLayout(tabs, rect)
    return tabs


class IDEFrame(MDIParentFrame):
    """IDEFrame Base Class"""

    def InitEditorToolbarItems(self):
        """
        Initialize dictionary with lists of elements that need to be shown
        if POU in particular programming language is edited.
        """
        # Define behaviour of each Toolbar item according to current POU body type
        # Informations meaning are in this order:
        #  - Item is toggled
        #  - PLCOpenEditor mode where item is displayed (could be more then one)
        #  - Item id
        #  - Item callback function name
        #  - Item icon filename
        #  - Item tooltip text
        self.EditorToolBarItems = {
            "FBD": [(True, FREEDRAWING_MODE | DRIVENDRAWING_MODE,
                     ID_PLCOPENEDITOREDITORTOOLBARMOTION, "OnMotionTool",
                     "move", _("Move the view")),
                    (True, FREEDRAWING_MODE | DRIVENDRAWING_MODE,
                     ID_PLCOPENEDITOREDITORTOOLBARCOMMENT, "OnCommentTool",
                     "add_comment", _("Create a new comment")),
                    (True, FREEDRAWING_MODE | DRIVENDRAWING_MODE,
                     ID_PLCOPENEDITOREDITORTOOLBARVARIABLE, "OnVariableTool",
                     "add_variable", _("Create a new variable")),
                    (True, FREEDRAWING_MODE | DRIVENDRAWING_MODE,
                     ID_PLCOPENEDITOREDITORTOOLBARBLOCK, "OnBlockTool",
                     "add_block", _("Create a new block")),
                    (True, FREEDRAWING_MODE | DRIVENDRAWING_MODE,
                     ID_PLCOPENEDITOREDITORTOOLBARCONNECTION, "OnConnectionTool",
                     "add_connection", _("Create a new connection"))],
            "LD": [(True, FREEDRAWING_MODE | DRIVENDRAWING_MODE,
                    ID_PLCOPENEDITOREDITORTOOLBARMOTION, "OnMotionTool",
                    "move", _("Move the view")),
                   (True, FREEDRAWING_MODE,
                    ID_PLCOPENEDITOREDITORTOOLBARCOMMENT, "OnCommentTool",
                    "add_comment", _("Create a new comment")),
                   (True, FREEDRAWING_MODE,
                    ID_PLCOPENEDITOREDITORTOOLBARPOWERRAIL, "OnPowerRailTool",
                    "add_powerrail", _("Create a new power rail")),
                   (False, DRIVENDRAWING_MODE,
                    ID_PLCOPENEDITOREDITORTOOLBARRUNG, "OnRungTool",
                    "add_rung", _("Create a new rung")),
                   (True, FREEDRAWING_MODE,
                    ID_PLCOPENEDITOREDITORTOOLBARCOIL, "OnCoilTool",
                    "add_coil", _("Create a new coil")),
                   (True, FREEDRAWING_MODE,
                    ID_PLCOPENEDITOREDITORTOOLBARICOIL, "OniCoilTool",
                    "add_icoil", _("Create a new icoil")),
                   (False, FREEDRAWING_MODE | DRIVENDRAWING_MODE,
                    ID_PLCOPENEDITOREDITORTOOLBARCONTACT, "OnContactTool",
                    "add_contact", _("Create a new contact")),
                   (False, DRIVENDRAWING_MODE,
                    ID_PLCOPENEDITOREDITORTOOLBARBRANCH, "OnBranchTool",
                    "add_branch", _("Create a new branch")),
                   (True, FREEDRAWING_MODE,
                    ID_PLCOPENEDITOREDITORTOOLBARVARIABLE, "OnVariableTool",
                    "add_variable", _("Create a new variable")),
                   (False, FREEDRAWING_MODE | DRIVENDRAWING_MODE,
                    ID_PLCOPENEDITOREDITORTOOLBARBLOCK, "OnBlockTool",
                    "add_block", _("Create a new block")),
                   (True, FREEDRAWING_MODE,
                    ID_PLCOPENEDITOREDITORTOOLBARCONNECTION, "OnConnectionTool",
                    "add_connection", _("Create a new connection"))],
            "SFC": [(True, FREEDRAWING_MODE | DRIVENDRAWING_MODE,
                     ID_PLCOPENEDITOREDITORTOOLBARMOTION, "OnMotionTool",
                     "move", _("Move the view")),
                    (True, FREEDRAWING_MODE | DRIVENDRAWING_MODE,
                     ID_PLCOPENEDITOREDITORTOOLBARCOMMENT, "OnCommentTool",
                     "add_comment", _("Create a new comment")),
                    (True, FREEDRAWING_MODE | DRIVENDRAWING_MODE,
                     ID_PLCOPENEDITOREDITORTOOLBARINITIALSTEP, "OnInitialStepTool",
                     "add_initial_step", _("Create a new initial step")),
                    (False, FREEDRAWING_MODE | DRIVENDRAWING_MODE,
                     ID_PLCOPENEDITOREDITORTOOLBARSTEP, "OnStepTool",
                     "add_step", _("Create a new step")),
                    (True, FREEDRAWING_MODE,
                     ID_PLCOPENEDITOREDITORTOOLBARTRANSITION, "OnTransitionTool",
                     "add_transition", _("Create a new transition")),
                    (False, FREEDRAWING_MODE | DRIVENDRAWING_MODE,
                     ID_PLCOPENEDITOREDITORTOOLBARACTIONBLOCK, "OnActionBlockTool",
                     "add_action", _("Create a new action block")),
                    (False, FREEDRAWING_MODE | DRIVENDRAWING_MODE,
                     ID_PLCOPENEDITOREDITORTOOLBARDIVERGENCE, "OnDivergenceTool",
                     "add_divergence", _("Create a new divergence")),
                    (False, FREEDRAWING_MODE | DRIVENDRAWING_MODE,
                     ID_PLCOPENEDITOREDITORTOOLBARJUMP, "OnJumpTool",
                     "add_jump", _("Create a new jump")),
                    (True, FREEDRAWING_MODE,
                     ID_PLCOPENEDITOREDITORTOOLBARVARIABLE, "OnVariableTool",
                     "add_variable", _("Create a new variable")),
                    (True, FREEDRAWING_MODE,
                     ID_PLCOPENEDITOREDITORTOOLBARBLOCK, "OnBlockTool",
                     "add_block", _("Create a new block")),
                    (True, FREEDRAWING_MODE,
                     ID_PLCOPENEDITOREDITORTOOLBARCONNECTION, "OnConnectionTool",
                     "add_connection", _("Create a new connection")),
                    (True, FREEDRAWING_MODE,
                     ID_PLCOPENEDITOREDITORTOOLBARPOWERRAIL, "OnPowerRailTool",
                     "add_powerrail", _("Create a new power rail")),
                    (True, FREEDRAWING_MODE,
                     ID_PLCOPENEDITOREDITORTOOLBARCONTACT, "OnContactTool",
                     "add_contact", _("Create a new contact"))],
            "ST": [],
            "IL": [],
            "debug": [(True, FREEDRAWING_MODE | DRIVENDRAWING_MODE,
                       ID_PLCOPENEDITOREDITORTOOLBARMOTION, "OnMotionTool",
                       "move", _("Move the view"))],
        }

    def _init_coll_MenuBar_Menus(self, parent):
        parent.Append(menu=self.FileMenu, title=_(u'&File'))
        parent.Append(menu=self.EditMenu, title=_(u'&Edit'))
        parent.Append(menu=self.DisplayMenu, title=_(u'&Display'))
        parent.Append(menu=self.HelpMenu, title=_(u'&Help'))

    def _init_coll_FileMenu_Items(self, parent):
        pass

    def _init_coll_AddMenu_Items(self, parent, add_config=True):
        AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDDATATYPE,
                   kind=wx.ITEM_NORMAL, text=_(u'&Data Type'))
        AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDFUNCTION,
                   kind=wx.ITEM_NORMAL, text=_(u'&Function'))
        AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDFUNCTIONBLOCK,
                   kind=wx.ITEM_NORMAL, text=_(u'Function &Block'))
        AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDPROGRAM,
                   kind=wx.ITEM_NORMAL, text=_(u'&Program'))
        AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDRESOURCE,
                   kind=wx.ITEM_NORMAL, text=_(u'&Resource'))
        if add_config:
            AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDCONFIGURATION,
                       kind=wx.ITEM_NORMAL, text=_(u'&Configuration'))

    def _init_coll_EditMenu_Items(self, parent):
        AppendMenu(parent, help='', id=wx.ID_UNDO,
                   kind=wx.ITEM_NORMAL, text=_(u'Undo') + '\tCTRL+Z')
        AppendMenu(parent, help='', id=wx.ID_REDO,
                   kind=wx.ITEM_NORMAL, text=_(u'Redo') + '\tCTRL+Y')
        parent.AppendSeparator()
        AppendMenu(parent, help='', id=wx.ID_CUT,
                   kind=wx.ITEM_NORMAL, text=_(u'Cut') + '\tCTRL+X')
        AppendMenu(parent, help='', id=wx.ID_COPY,
                   kind=wx.ITEM_NORMAL, text=_(u'Copy') + '\tCTRL+C')
        AppendMenu(parent, help='', id=wx.ID_PASTE,
                   kind=wx.ITEM_NORMAL, text=_(u'Paste') + '\tCTRL+V')
        parent.AppendSeparator()
        AppendMenu(parent, help='', id=wx.ID_FIND,
                   kind=wx.ITEM_NORMAL, text=_(u'Find') + '\tCTRL+F')
        AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUFINDNEXT,
                   kind=wx.ITEM_NORMAL, text=_(u'Find Next') + '\tCTRL+K')
        AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUFINDPREVIOUS,
                   kind=wx.ITEM_NORMAL, text=_(u'Find Previous') + '\tCTRL+SHIFT+K')
        parent.AppendSeparator()
        AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT,
                   kind=wx.ITEM_NORMAL, text=_(u'Search in Project') + '\tCTRL+SHIFT+F')
        parent.AppendSeparator()
        add_menu = wx.Menu(title='')
        self._init_coll_AddMenu_Items(add_menu)
        ID_ADD = parent.Append(wx.ID_ADD, _("&Add Element"), add_menu)
        AppendMenu(parent, help='', id=wx.ID_SELECTALL,
                   kind=wx.ITEM_NORMAL, text=_(u'Select All') + '\tCTRL+A')
        AppendMenu(parent, help='', id=wx.ID_DELETE,
                   kind=wx.ITEM_NORMAL, text=_(u'&Delete'))
        parent.AppendSeparator()
        AppendMenu(parent, help='', id=wx.ID_FLOPPY, kind=wx.ITEM_NORMAL, text=_('platform update'))
        self.Bind(wx.EVT_MENU, self.OnUndoMenu, id=wx.ID_UNDO)
        self.Bind(wx.EVT_MENU, self.OnRedoMenu, id=wx.ID_REDO)
        # self.Bind(wx.EVT_MENU, self.OnEnableUndoRedoMenu, id=ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO)
        self.Bind(wx.EVT_MENU, self.OnCutMenu, id=wx.ID_CUT)
        self.Bind(wx.EVT_MENU, self.OnCopyMenu, id=wx.ID_COPY)
        self.Bind(wx.EVT_MENU, self.OnPasteMenu, id=wx.ID_PASTE)
        self.Bind(wx.EVT_MENU, self.OnFindMenu, id=wx.ID_FIND)
        self.Bind(wx.EVT_MENU, self.OnFindNextMenu,
                  id=ID_PLCOPENEDITOREDITMENUFINDNEXT)
        self.Bind(wx.EVT_MENU, self.OnFindPreviousMenu,
                  id=ID_PLCOPENEDITOREDITMENUFINDPREVIOUS)
        self.Bind(wx.EVT_MENU, self.OnSearchInProjectMenu,
                  id=ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT)
        self.Bind(wx.EVT_MENU, self.OnAddDataTypeMenu,
                  id=ID_PLCOPENEDITOREDITMENUADDDATATYPE)
        self.Bind(wx.EVT_MENU, self.GenerateAddPouFunction("function"),
                  id=ID_PLCOPENEDITOREDITMENUADDFUNCTION)
        self.Bind(wx.EVT_MENU, self.GenerateAddPouFunction("functionBlock"),
                  id=ID_PLCOPENEDITOREDITMENUADDFUNCTIONBLOCK)
        self.Bind(wx.EVT_MENU, self.GenerateAddPouFunction("program"),
                  id=ID_PLCOPENEDITOREDITMENUADDPROGRAM)
        self.Bind(wx.EVT_MENU, self.AddResourceMenu,
                  id=ID_PLCOPENEDITOREDITMENUADDRESOURCE)
        self.Bind(wx.EVT_MENU, self.OnAddConfigurationMenu,
                  id=ID_PLCOPENEDITOREDITMENUADDCONFIGURATION)
        self.Bind(wx.EVT_MENU, self.OnSelectAllMenu, id=wx.ID_SELECTALL)
        self.Bind(wx.EVT_MENU, self.OnDeleteMenu, id=wx.ID_DELETE)
        self.Bind(wx.EVT_MENU, self.OnPlatformUpdate, id=wx.ID_FLOPPY)

        self.AddToMenuToolBar([(wx.ID_UNDO, "undo", _(u'Undo'), None),
                               (wx.ID_REDO, "redo", _(u'Redo'), None),
                               None,
                               (wx.ID_CUT, "cut", _(u'Cut'), None),
                               (wx.ID_COPY, "copy", _(u'Copy'), None),
                               (wx.ID_PASTE, "paste", _(u'Paste'), None),
                               None,
                               (ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, "find", _(u'Search in Project'), None),
                               (ID_PLCOPENEDITORDISPLAYMENUFULLSCREEN, "fullscreen", _(u'Toggle fullscreen mode'),
                                None)])

    def _init_coll_DisplayMenu_Items(self, parent):
        AppendMenu(parent, help='', id=wx.ID_REFRESH,
                   kind=wx.ITEM_NORMAL, text=_(u'Refresh') + '\tCTRL+R')
        if self.EnableDebug:
            AppendMenu(parent, help='', id=wx.ID_CLEAR,
                       kind=wx.ITEM_NORMAL, text=_(u'Clear Errors') + '\tCTRL+K')
        parent.AppendSeparator()
        zoommenu = wx.Menu(title='')
        parent.Append(wx.ID_ZOOM_FIT, _("Zoom"), zoommenu)
        for idx, value in enumerate(ZOOM_FACTORS):
            new_id = wx.NewIdRef()
            AppendMenu(zoommenu, help='', id=new_id,
                       kind=wx.ITEM_RADIO, text=str(int(round(value * 100))) + "%")
            self.Bind(wx.EVT_MENU, self.GenerateZoomFunction(idx), id=new_id)

        parent.AppendSeparator()
        AppendMenu(parent, help='', id=ID_PLCOPENEDITORDISPLAYMENUSWITCHPERSPECTIVE,
                   kind=wx.ITEM_NORMAL, text=_(u'Switch perspective') + '\tF12')
        self.Bind(wx.EVT_MENU, self.SwitchPerspective, id=ID_PLCOPENEDITORDISPLAYMENUSWITCHPERSPECTIVE)

        AppendMenu(parent, help='', id=ID_PLCOPENEDITORDISPLAYMENUFULLSCREEN,
                   kind=wx.ITEM_NORMAL, text=_(u'Full screen') + '\tShift-F12')
        self.Bind(wx.EVT_MENU, self.SwitchFullScrMode, id=ID_PLCOPENEDITORDISPLAYMENUFULLSCREEN)

        AppendMenu(parent, help='', id=ID_PLCOPENEDITORDISPLAYMENURESETPERSPECTIVE,
                   kind=wx.ITEM_NORMAL, text=_(u'Reset Perspective'))
        self.Bind(wx.EVT_MENU, self.OnResetPerspective, id=ID_PLCOPENEDITORDISPLAYMENURESETPERSPECTIVE)

        self.Bind(wx.EVT_MENU, self.OnRefreshMenu, id=wx.ID_REFRESH)
        if self.EnableDebug:
            self.Bind(wx.EVT_MENU, self.OnClearErrorsMenu, id=wx.ID_CLEAR)

    def _init_coll_HelpMenu_Items(self, parent):
        pass

    def _init_utils(self):
        self.MenuBar = wx.MenuBar()

        self.FileMenu = wx.Menu(title='')
        self.EditMenu = wx.Menu(title='')
        self.DisplayMenu = wx.Menu(title='')
        self.HelpMenu = wx.Menu(title='')

        self._init_coll_MenuBar_Menus(self.MenuBar)
        self._init_coll_FileMenu_Items(self.FileMenu)
        self._init_coll_EditMenu_Items(self.EditMenu)
        self._init_coll_DisplayMenu_Items(self.DisplayMenu)
        self._init_coll_HelpMenu_Items(self.HelpMenu)

    def _init_icon(self, parent):
        if self.icon:
            self.SetIcon(self.icon)
        elif parent and parent.icon:
            self.SetIcon(parent.icon)

    def _init_ctrls(self, prnt):
        self._init_icon(prnt)
        self.SetClientSize((wx.Size(1000, 600)))
        self.Bind(wx.EVT_ACTIVATE, self.OnActivated)

        self.TabsImageList = wx.ImageList(31, 16)
        self.TabsImageListIndexes = {}

        # -----------------------------------------------------------------------
        #                          Creating main structure
        # -----------------------------------------------------------------------

        self.AUIManager = wx.aui.AuiManager(self)
        self.AUIManager.SetDockSizeConstraint(0.5, 0.5)
        self.Panes = {}

        self.LeftNoteBook = wx.aui.AuiNotebook(
            self, ID_PLCOPENEDITORLEFTNOTEBOOK,
            style=(wx.aui.AUI_NB_TOP |
                   wx.aui.AUI_NB_TAB_SPLIT |
                   wx.aui.AUI_NB_TAB_MOVE |
                   wx.aui.AUI_NB_SCROLL_BUTTONS |
                   wx.aui.AUI_NB_TAB_EXTERNAL_MOVE))
        self.LeftNoteBook.Bind(wx.aui.EVT_AUINOTEBOOK_ALLOW_DND,
                               self.OnAllowNotebookDnD)
        self.AUIManager.AddPane(
            self.LeftNoteBook,
            wx.aui.AuiPaneInfo().Name("ProjectPane").Left().Layer(1).
            BestSize((wx.Size(300, 500))).CloseButton(False))

        self.BottomNoteBook = wx.aui.AuiNotebook(
            self, ID_PLCOPENEDITORBOTTOMNOTEBOOK,
            style=(wx.aui.AUI_NB_TOP |
                   wx.aui.AUI_NB_TAB_SPLIT |
                   wx.aui.AUI_NB_TAB_MOVE |
                   wx.aui.AUI_NB_SCROLL_BUTTONS |
                   wx.aui.AUI_NB_TAB_EXTERNAL_MOVE))
        self.BottomNoteBook.Bind(wx.aui.EVT_AUINOTEBOOK_ALLOW_DND,
                                 self.OnAllowNotebookDnD)
        self.AUIManager.AddPane(
            self.BottomNoteBook,
            wx.aui.AuiPaneInfo().Name("ResultPane").Bottom().Layer(0).
            BestSize((wx.Size(800, 300))).CloseButton(False))

        self.RightNoteBook = wx.aui.AuiNotebook(
            self, ID_PLCOPENEDITORRIGHTNOTEBOOK,
            style=(wx.aui.AUI_NB_TOP |
                   wx.aui.AUI_NB_TAB_SPLIT |
                   wx.aui.AUI_NB_TAB_MOVE |
                   wx.aui.AUI_NB_SCROLL_BUTTONS |
                   wx.aui.AUI_NB_TAB_EXTERNAL_MOVE))
        self.RightNoteBook.Bind(wx.aui.EVT_AUINOTEBOOK_ALLOW_DND,
                                self.OnAllowNotebookDnD)
        self.AUIManager.AddPane(
            self.RightNoteBook,
            wx.aui.AuiPaneInfo().Name("LibraryPane").Right().Layer(0).
            BestSize((wx.Size(250, 400))).CloseButton(False))

        self.maxPaneOldDir = None
        self.Bind(wx.aui.EVT_AUI_PANE_MAXIMIZE, self.onMaximizeAuiPane)
        self.Bind(wx.aui.EVT_AUI_PANE_RESTORE, self.onRestoreAuiPane)
        if USETAB:
            self.TabsOpened = wx.aui.AuiNotebook(
                self, ID_PLCOPENEDITORTABSOPENED,
                style=(wx.aui.AUI_NB_DEFAULT_STYLE |
                       wx.aui.AUI_NB_WINDOWLIST_BUTTON))
            self.TabsOpened.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGING,
                                 self.OnPouSelectedChanging)
            self.TabsOpened.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.OnPouSelectedChanged)
            self.TabsOpened.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.OnPageClose)
            self.TabsOpened.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CLOSED, self.OnAuiNotebookPageClose)
            self.TabsOpened.Bind(wx.aui.EVT_AUINOTEBOOK_END_DRAG,
                                 self.OnPageDragged)
            self.AUIManager.AddPane(self.TabsOpened,
                                    wx.aui.AuiPaneInfo().CentrePane().Name("TabsPane"))
        else:
            self.TabsOpened = None

        # -----------------------------------------------------------------------
        #                    Creating PLCopen Project Types Tree
        # -----------------------------------------------------------------------

        self.MainTabs = {}

        self.ProjectPanel = wx.SplitterWindow(
            id=ID_PLCOPENEDITORPROJECTPANEL,
            name='ProjectPanel', parent=self.LeftNoteBook,
            size=(wx.Size(0, 0)), style=wx.SP_3D)

        self.ProjectTree = CustomTree(id=ID_PLCOPENEDITORPROJECTTREE,
                                      name='ProjectTree',
                                      parent=self.ProjectPanel,
                                      pos=wx.Point(0, 0), size=(wx.Size(0, 0)),
                                      style=wx.SUNKEN_BORDER,
                                      agwStyle=(wx.TR_HAS_BUTTONS |
                                                wx.TR_SINGLE |
                                                wx.TR_EDIT_LABELS))
        self.ProjectTree.SetBackgroundBitmap(GetBitmap("custom_tree_background"),
                                             wx.ALIGN_RIGHT | wx.ALIGN_BOTTOM)
        add_menu = wx.Menu()
        self._init_coll_AddMenu_Items(add_menu)
        self.ProjectTree.SetAddMenu(add_menu)
        self.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, self.OnProjectTreeRightUp,
                  id=ID_PLCOPENEDITORPROJECTTREE)
        self.ProjectTree.Bind(wx.EVT_LEFT_UP, self.OnProjectTreeLeftUp)
        self.Bind(wx.EVT_TREE_SEL_CHANGING, self.OnProjectTreeItemChanging,
                  id=ID_PLCOPENEDITORPROJECTTREE)
        self.Bind(wx.EVT_TREE_BEGIN_DRAG, self.OnProjectTreeBeginDrag,
                  id=ID_PLCOPENEDITORPROJECTTREE)
        self.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT, self.OnProjectTreeItemBeginEdit,
                  id=ID_PLCOPENEDITORPROJECTTREE)
        self.Bind(wx.EVT_TREE_END_LABEL_EDIT, self.OnProjectTreeItemEndEdit,
                  id=ID_PLCOPENEDITORPROJECTTREE)
        self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnProjectTreeItemActivated,
                  id=ID_PLCOPENEDITORPROJECTTREE)
        self.ProjectTree.Bind(wx.EVT_MOTION, self.OnProjectTreeMotion)

        # -----------------------------------------------------------------------
        #        Creating PLCopen Project POU Instance Variables Panel
        # -----------------------------------------------------------------------

        self.PouInstanceVariablesPanel = PouInstanceVariablesPanel(self.ProjectPanel, self, self.Controler,
                                                                   self.EnableDebug)

        self.MainTabs["ProjectPanel"] = (self.ProjectPanel, _("Project"))
        self.LeftNoteBook.AddPage(*self.MainTabs["ProjectPanel"])

        self.ProjectPanel.SplitHorizontally(self.ProjectTree, self.PouInstanceVariablesPanel, 300)

        # -----------------------------------------------------------------------
        #                            Creating Tool Bar
        # -----------------------------------------------------------------------

        MenuToolBar = wx.ToolBar(self, ID_PLCOPENEDITOREDITORMENUTOOLBAR,
                                 wx.DefaultPosition, wx.DefaultSize,
                                 wx.TB_FLAT | wx.TB_NODIVIDER | wx.NO_BORDER)
        MenuToolBar.SetToolBitmapSize((wx.Size(25, 25)))
        MenuToolBar.Realize()
        self.Panes["MenuToolBar"] = MenuToolBar
        self.AUIManager.AddPane(MenuToolBar, wx.aui.AuiPaneInfo().
                                Name("MenuToolBar").Caption(_("Menu ToolBar")).
                                ToolbarPane().Top().
                                LeftDockable(False).RightDockable(False))

        EditorToolBar = wx.ToolBar(self, ID_PLCOPENEDITOREDITORTOOLBAR,
                                   wx.DefaultPosition, wx.DefaultSize,
                                   wx.TB_FLAT | wx.TB_NODIVIDER | wx.NO_BORDER)
        EditorToolBar.SetToolBitmapSize((wx.Size(25, 25)))
        EditorToolBar.AddRadioTool(ID_PLCOPENEDITOREDITORTOOLBARSELECTION, 'label',
                                   GetBitmap("select"),
                                   wx.NullBitmap,
                                   _("Select an object"))
        EditorToolBar.Realize()
        self.Panes["EditorToolBar"] = EditorToolBar
        self.AUIManager.AddPane(EditorToolBar, wx.aui.AuiPaneInfo().
                                Name("EditorToolBar").Caption(_("Editor ToolBar")).
                                ToolbarPane().Top().Position(1).
                                LeftDockable(False).RightDockable(False))

        self.Bind(wx.EVT_MENU, self.OnSelectionTool,
                  id=ID_PLCOPENEDITOREDITORTOOLBARSELECTION)

        # -----------------------------------------------------------------------
        #                            Creating Search Panel
        # -----------------------------------------------------------------------

        self.SearchResultPanel = SearchResultPanel(self.BottomNoteBook, self)
        self.MainTabs["SearchResultPanel"] = (self.SearchResultPanel, _("Search"))
        self.BottomNoteBook.AddPage(*self.MainTabs["SearchResultPanel"])

        # -----------------------------------------------------------------------
        #                            Creating Library Panel
        # -----------------------------------------------------------------------

        self.LibraryPanel = LibraryPanel(self, True)
        self.MainTabs["LibraryPanel"] = (self.LibraryPanel, _("Library"))
        self.RightNoteBook.AddPage(*self.MainTabs["LibraryPanel"])

        self._init_utils()
        self.SetMenuBar(self.MenuBar)

        if self.EnableDebug:
            self.DebugVariablePanel = DebugVariablePanel(self.RightNoteBook, self.Controler, self)
            self.MainTabs["DebugVariablePanel"] = (self.DebugVariablePanel, _("Debugger"))
            self.RightNoteBook.AddPage(*self.MainTabs["DebugVariablePanel"])

        self.AUIManager.Update()

    def onMaximizeAuiPane(self, event):
        pane = event.GetPane()
        self.maxPaneOldDir = pane.dock_direction
        pane.Right()

    def onRestoreAuiPane(self, event):
        pane = event.GetPane()
        if self.maxPaneOldDir:
            pane.dock_direction = self.maxPaneOldDir
            self.maxPaneOldDir = None

    def __init__(self, parent, enable_debug=False):
        wx.MDIParentFrame.__init__(self, parent=parent, id=ID_PLCOPENEDITOR, pos=wx.DefaultPosition,
                                   size=wx.Size(1000, 600), style=wx.DEFAULT_FRAME_STYLE)
        self.SetSize((wx.Size(1000, 600)))
        self.UNEDITABLE_NAMES_DICT = dict([(_(n), n) for n in UNEDITABLE_NAMES])

        self.Controler = None
        self.Config = wx.ConfigBase.Get()
        self.EnableDebug = enable_debug

        self.InitEditorToolbarItems()
        self._init_ctrls(parent)

        # Define Tree item icon list
        self.TreeImageList = wx.ImageList(16, 16)
        self.TreeImageDict = {}

        # Icons for languages
        for language in LANGUAGES:
            self.TreeImageDict[language] = self.TreeImageList.Add(GetBitmap(language))

        # Icons for other items
        for imgname, itemtype in [
            # editables
            ("PROJECT", ITEM_PROJECT),
            # ("POU",          ITEM_POU),
            # ("VARIABLE",     ITEM_VARIABLE),
            ("TRANSITION", ITEM_TRANSITION),
            ("ACTION", ITEM_ACTION),
            ("CONFIGURATION", ITEM_CONFIGURATION),
            ("RESOURCE", ITEM_RESOURCE),
            ("DATATYPE", ITEM_DATATYPE),
            # uneditables
            ("DATATYPES", ITEM_DATATYPES),
            ("FUNCTION", ITEM_FUNCTION),
            ("FUNCTIONBLOCK", ITEM_FUNCTIONBLOCK),
            ("PROGRAM", ITEM_PROGRAM),
            ("VAR_LOCAL", ITEM_VAR_LOCAL),
            ("VAR_LOCAL", ITEM_VAR_GLOBAL),
            ("VAR_LOCAL", ITEM_VAR_EXTERNAL),
            ("VAR_LOCAL", ITEM_VAR_TEMP),
            ("VAR_INPUT", ITEM_VAR_INPUT),
            ("VAR_OUTPUT", ITEM_VAR_OUTPUT),
            ("VAR_INOUT", ITEM_VAR_INOUT),
            ("TRANSITIONS", ITEM_TRANSITIONS),
            ("ACTIONS", ITEM_ACTIONS),
            ("CONFIGURATIONS", ITEM_CONFIGURATIONS),
            ("RESOURCES", ITEM_RESOURCES),
            ("PROPERTIES", ITEM_PROPERTIES)]:
            self.TreeImageDict[itemtype] = self.TreeImageList.Add(GetBitmap(imgname))

        # Assign icon list to TreeCtrls
        self.ProjectTree.SetImageList(self.TreeImageList)
        self.PouInstanceVariablesPanel.SetTreeImageList(self.TreeImageList)

        self.CurrentEditorToolBar = []
        self.CurrentMenu = None
        self.SelectedItem = None
        self.LastToolTipItem = None
        self.SearchParams = None
        self.Highlights = {}
        self.DrawingMode = FREEDRAWING_MODE
        # self.DrawingMode = DRIVENDRAWING_MODE
        self.AuiTabCtrl = []

        # Save default perspective
        notebooks = {}
        for notebook, entry_name in [(self.LeftNoteBook, "leftnotebook"),
                                     (self.BottomNoteBook, "bottomnotebook"),
                                     (self.RightNoteBook, "rightnotebook")]:
            notebooks[entry_name] = self.SaveTabLayout(notebook)
        self.DefaultPerspective = {
            "perspective": self.AUIManager.SavePerspective(),
            "notebooks": notebooks,
        }

        # Initialize Printing configuring elements
        self.PrintData = wx.PrintData()
        self.PrintData.SetPaperId(wx.PAPER_A4)
        self.PrintData.SetPrintMode(wx.PRINT_MODE_PRINTER)
        self.PageSetupData = wx.PageSetupDialogData(self.PrintData)
        self.PageSetupData.SetMarginTopLeft(wx.Point(10, 15))
        self.PageSetupData.SetMarginBottomRight(wx.Point(10, 20))

        self.SetRefreshFunctions()
        self.SetDeleteFunctions()

        wx.CallAfter(self.InitFindDialog)
        AsyncBind(wx.EVT_CLOSE, self.OnClose, self)

    def OnWindowChanged(self, event):
        self.PouInstanceVariablesPanel.RefreshView()
        event.Skip()

    async def OnClose(self, event):
        self.AUIManager.UnInit()
        self.Destroy()

    def __del__(self):
        self.FindDialog.Destroy()

    def InitFindDialog(self):
        self.FindDialog = FindInPouDialog(self)
        self.FindDialog.Hide()

    def Show(self):
        wx.Frame.Show(self)
        wx.CallAfter(self.RestoreLastState)

    def OnActivated(self, event):
        if event.GetActive():
            wx.CallAfter(self._Refresh, TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU)
        event.Skip()

    def SelectTab(self, tab):
        for notebook in [self.LeftNoteBook, self.BottomNoteBook, self.RightNoteBook]:
            idx = notebook.GetPageIndex(tab)
            if idx != wx.NOT_FOUND and idx != notebook.GetSelection():
                notebook.SetSelection(idx)
                return

    # -------------------------------------------------------------------------------
    #                Saving and restoring frame organization functions
    # -------------------------------------------------------------------------------

    def GetTabInfos(self, tab):
        for page_name, (page_ref, _page_title) in self.MainTabs.items():
            if page_ref == tab:
                return "main", page_name
        return None

    def SaveTabLayout(self, notebook):
        tabs = []
        for child in notebook.GetChildren():
            if isinstance(child, wx.aui.AuiTabCtrl) and child.GetPageCount() > 0:
                pos = child.GetPosition()
                tab = {"pos": (pos.x, pos.y), "pages": []}
                tab_size = child.GetSize()
                for page_idx in range(child.GetPageCount()):
                    page = child.GetWindowFromIdx(page_idx)
                    if "size" not in tab:
                        tab["size"] = (tab_size[0], tab_size[1] + page.GetSize()[1])
                    tab_infos = self.GetTabInfos(page)
                    if tab_infos is not None:
                        tab["pages"].append((tab_infos, page_idx == child.GetActivePage()))
                tabs.append(tab)

        def pos(item):
            return item['pos']

        tabs.sort(key=pos)
        size = notebook.GetSize()
        return ComputeTabsLayout(tabs, wx.Rect(1, 1, size[0] - NOTEBOOK_BORDER, size[1] - NOTEBOOK_BORDER))

    def LoadTab(self, notebook, page_infos):
        if page_infos[0] == "main":
            infos = self.MainTabs.get(page_infos[1])
            if infos is not None:
                page_ref, page_title = infos
                notebook.AddPage(page_ref, page_title)
                return notebook.GetPageIndex(page_ref)
        elif page_infos[0] == "editor":
            tagname = page_infos[1]
            page_ref = self.EditProjectElement(GetElementType(tagname), tagname)
            if page_ref is not None:
                page_ref.RefreshView()
                return notebook.GetPageIndex(page_ref)
        elif page_infos[0] == "debug":
            instance_path = page_infos[1]
            instance_infos = self.Controler.GetInstanceInfos(instance_path, self.EnableDebug)
            if instance_infos is not None:
                return notebook.GetPageIndex(
                    self.OpenDebugViewer(instance_infos["class"], instance_path, instance_infos["type"]))
        return None

    def LoadTabLayout(self, notebook, tabs, mode="all", first_index=None):
        if isinstance(tabs, list):
            if len(tabs) == 0:
                return
            raise ValueError("Not supported")

        if "split" in tabs:
            self.LoadTabLayout(notebook, tabs["others"])

            split_dir, _split_ratio = tabs["split"]
            first_index = self.LoadTabLayout(notebook, tabs["tab"], mode="first")
            notebook.Split(first_index, split_dir)
            self.LoadTabLayout(notebook, tabs["tab"], mode="others", first_index=first_index)

        elif mode == "first":
            return self.LoadTab(notebook, tabs["pages"][0][0])
        else:
            selected = first_index
            if mode == "others":
                add_tabs = tabs["pages"][1:]
            else:
                add_tabs = tabs["pages"]
            for page_infos, page_selected in add_tabs:
                page_idx = self.LoadTab(notebook, page_infos)
                if page_selected:
                    selected = page_idx
            if selected is not None:
                wx.CallAfter(notebook.SetSelection, selected)

    def ResetPerspective(self):
        if self.DefaultPerspective is not None:
            self.AUIManager.LoadPerspective(self.DefaultPerspective["perspective"])

            for notebook in [self.LeftNoteBook, self.BottomNoteBook, self.RightNoteBook]:
                for dummy in range(notebook.GetPageCount()):
                    notebook.RemovePage(0)

            notebooks = self.DefaultPerspective["notebooks"]
            for notebook, entry_name in [(self.LeftNoteBook, "leftnotebook"),
                                         (self.BottomNoteBook, "bottomnotebook"),
                                         (self.RightNoteBook, "rightnotebook")]:
                self.LoadTabLayout(notebook, notebooks.get(entry_name))

            self._Refresh(EDITORTOOLBAR)

    def RestoreLastState(self):
        frame_size = None
        if self.Config.HasEntry("framesize"):
            frame_size = pickle.loads(self.Config.Read("framesize").encode())

        if frame_size is None:
            self.Maximize()
        else:
            self.SetClientSize((frame_size))

    def SaveLastState(self):
        if not self.IsMaximized():
            self.Config.Write("framesize", pickle.dumps(self.GetClientSize(), 0))
        elif self.Config.HasEntry("framesize"):
            self.Config.DeleteEntry("framesize")

        self.Config.Flush()

    # -------------------------------------------------------------------------------
    #                               General Functions
    # -------------------------------------------------------------------------------

    def SetRefreshFunctions(self):
        self.RefreshFunctions = {
            TITLE: self.RefreshTitle,
            EDITORTOOLBAR: self.RefreshEditorToolBar,
            FILEMENU: self.RefreshFileMenu,
            EDITMENU: self.RefreshEditMenu,
            DISPLAYMENU: self.RefreshDisplayMenu,
            PROJECTTREE: self.RefreshProjectTree,
            POUINSTANCEVARIABLESPANEL: self.RefreshPouInstanceVariablesPanel,
            LIBRARYTREE: self.RefreshLibraryPanel,
            SCALING: self.RefreshScaling,
            PAGETITLES: self.RefreshPageTitles}

    def _Refresh(self, *elements):
        """Call Editor refresh functions.

        :param elements: List of elements to refresh.
        """
        try:
            for element in elements:
                self.RefreshFunctions[element]()
        except Exception as ex:
            logger.debug(traceback.format_exc())

    def OnPageClose(self, event):
        """Callback function when AUINotebook Page closed with CloseButton

        :param event: AUINotebook Event.
        """
        if USETAB:
            selected = self.TabsOpened.GetSelection()
        if selected > -1:
            window = self.TabsOpened.GetPage(selected)

            if window.CheckSaveBeforeClosing():

                # Refresh all window elements that have changed
                wx.CallAfter(self._Refresh, TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU)
                wx.CallAfter(self.RefreshTabCtrlEvent)
                wx.CallAfter(self.CloseFindInPouDialog)
                event.Skip()
            else:
                event.Veto()
        else:
            selected = self.ActiveChild
        if selected and isinstance(selected.Children[0], Viewer):
            window = selected.Children[0]
            if window.CheckSaveBeforeClosing():
                wx.CallAfter(self._Refresh, TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU)
                wx.CallAfter(self.RefreshTabCtrlEvent)
                wx.CallAfter(self.CloseFindInPouDialog)
                event.Skip()
            else:
                event.Veto()

    def OnAuiNotebookPageClose(self, event):
        auinotebook = event.GetEventObject()
        page_idx = event.GetSelection()
        auinotebook.RemovePage(page_idx)
        auinotebook.DeletePage(page_idx)

    def GetCopyBuffer(self, primary_selection=False):
        data = None
        if primary_selection and wx.Platform == '__WXMSW__':
            return data
        else:
            wx.TheClipboard.UsePrimarySelection(primary_selection)

        if not wx.TheClipboard.IsOpened():
            dataobj = wx.TextDataObject()
            if wx.TheClipboard.Open():
                success = False
                try:
                    success = wx.TheClipboard.GetData(dataobj)
                except wx._core.PyAssertionError:
                    pass
                wx.TheClipboard.Close()
                if success:
                    data = dataobj.GetText()
        return data

    def SetCopyBuffer(self, text, primary_selection=False):
        if primary_selection and wx.Platform == '__WXMSW__':
            return
        else:
            wx.TheClipboard.UsePrimarySelection(primary_selection)
        if not wx.TheClipboard.IsOpened():
            data = wx.TextDataObject()
            data.SetText(text)
            if wx.TheClipboard.Open():
                wx.TheClipboard.SetData(data)
                wx.TheClipboard.Flush()
                wx.TheClipboard.Close()
        wx.CallAfter(self.RefreshEditMenu)

    def GetDrawingMode(self):
        return self.DrawingMode

    def RefreshScaling(self):
        if USETAB:
            for i in range(self.TabsOpened.GetPageCount()):
                editor = self.TabsOpened.GetPage(i)
                editor.RefreshScaling()
        else:
            for i in self.ChildWindow():
                editor = i.Children[0]
                editor.RefreshScaling()

    def EditProjectSettings(self):
        old_values = self.Controler.GetProjectProperties()
        dialog = ProjectDialog(self)
        dialog.SetValues(old_values)
        if dialog.ShowModal() == wx.ID_OK:
            new_values = dialog.GetValues()
            new_values["creationDateTime"] = old_values["creationDateTime"]
            if new_values != old_values:
                self.Controler.SetProjectProperties(None, new_values)
                self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU,
                              PROJECTTREE, POUINSTANCEVARIABLESPANEL, SCALING)
        dialog.Destroy()

    # -------------------------------------------------------------------------------
    #                            Notebook Unified Functions
    # -------------------------------------------------------------------------------

    def AddPage(self, window, text):
        """Function that add a tab in Notebook, calling refresh for tab DClick event
        for wx.aui.AUINotebook.

    :param window: Panel to display in tab.
    :param text: title for the tab ctrl.
    """
        if USETAB:
            self.TabsOpened.AddPage(window, text)
        self.RefreshTabCtrlEvent()

    def DeletePage(self, window):
        if USETAB:
            for idx in range(self.TabsOpened.GetPageCount()):
                if self.TabsOpened.GetPage(idx) == window:
                    self.TabsOpened.DeletePage(idx)
                    self.RefreshTabCtrlEvent()
                    return
        else:
            for idx in self.ChildWindow():
                if idx.Children[0] == window:
                    idx.Close()
        self.RefreshTabCtrlEvent()

    def DeleteAllPages(self):
        """Function that fix difference in deleting all tabs between
        wx.Notebook and wx.aui.AUINotebook.
        """
        if USETAB:
            for dummy in range(self.TabsOpened.GetPageCount()):
                self.TabsOpened.DeletePage(0)
        else:
            for dummy in self.ChildWindow():
                if not isinstance(dummy.Children[0], [Viewer, Viewer]):
                    dummy.Close()
        self.RefreshTabCtrlEvent()

    def SetPageBitmap(self, idx, bitmap):
        """Function that fix difference in setting picture on tab between
        wx.Notebook and wx.aui.AUINotebook.

        :param idx: Tab index.
        :param bitmap: wx.Bitmap to define on tab.
        :returns: True if operation succeeded
        """
        if USETAB:
            return self.TabsOpened.SetPageBitmap(idx, bitmap)

    # -------------------------------------------------------------------------------
    #                         Dialog Message Functions
    # -------------------------------------------------------------------------------

    def ShowErrorMessage(self, message):
        """Function displaying an Error dialog in editor.

        :param message: The message to display.
        """
        dialog = wx.MessageDialog(self, message, _("Error"), wx.OK | wx.ICON_ERROR)
        dialog.ShowModal()
        dialog.Destroy()

    def CheckSaveBeforeClosing(self, title=_("Close Project")):
        """Function displaying an question dialog if project is not saved"

    :returns: False if closing cancelled.
    """
        if not self.Controler.ProjectIsSaved():
            dialog = wx.MessageDialog(self, _("There are changes, do you want to save?"), title,
                                      wx.YES_NO | wx.CANCEL | wx.ICON_QUESTION)
            answer = dialog.ShowModal()
            dialog.Destroy()
            if answer == wx.ID_YES:
                self.SaveProject()
            elif answer == wx.ID_CANCEL:
                return False
            if USETAB:
                for idx in range(self.TabsOpened.GetPageCount()):
                    window = self.TabsOpened.GetPage(idx)
                    if not window.CheckSaveBeforeClosing():
                        return False
            else:
                for idx in self.ChildWindow():
                    window = idx
                    if not window.CheckSaveBeforeClosing():
                        return False
        return True

    # -------------------------------------------------------------------------------
    #                            File Menu Functions
    # -------------------------------------------------------------------------------

    def RefreshFileMenu(self):
        pass

    def ResetView(self):
        self.DeleteAllPages()
        self.ProjectTree.DeleteAllItems()
        self.ProjectTree.Enable(False)
        self.PouInstanceVariablesPanel.ResetView()
        self.LibraryPanel.ResetTree()
        self.LibraryPanel.SetController(None)
        if self.EnableDebug:
            self.DebugVariablePanel.ResetView()
        self.Controler = None

    def OnCloseTabMenu(self, event):
        if USETAB:
            selected = self.TabsOpened.GetSelection()
            if selected >= 0:
                self.TabsOpened.DeletePage(selected)
                if self.TabsOpened.GetPageCount() > 0:
                    new_index = min(selected, self.TabsOpened.GetPageCount() - 1)
                    self.TabsOpened.SetSelection(new_index)
        else:
            selected = self.ActiveChild
            if selected:
                selected.Close()
                if len(self.ChildWindow()) > 0:
                    self.ActivateNext()
        # Refresh all window elements that have changed
        self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU)
        self.RefreshTabCtrlEvent()

    def OnPageSetupMenu(self, event):
        dialog = wx.PageSetupDialog(self, self.PageSetupData)
        if dialog.ShowModal() == wx.ID_OK:
            self.PageSetupData = wx.PageSetupDialogData(dialog.GetPageSetupData())
            self.PrintData = wx.PrintData(self.PageSetupData.GetPrintData())
        dialog.Destroy()

    def OnPreviewMenu(self, event):
        if USETAB:
            selected = self.TabsOpened.GetSelection()
            if selected != -1:
                window = self.TabsOpened.GetPage(selected)
                data = wx.PrintDialogData(self.PrintData)
                properties = self.Controler.GetProjectProperties(window.IsDebugging())
                page_size = map(int, properties["pageSize"])
                margins = (self.PageSetupData.GetMarginTopLeft(), self.PageSetupData.GetMarginBottomRight())
                printout = GraphicPrintout(window, page_size, margins, True)
                printout2 = GraphicPrintout(window, page_size, margins, True)
                preview = wx.PrintPreview(printout, printout2, data)

                if preview.Ok():
                    preview_frame = wx.PreviewFrame(preview, self, _("Print preview"),
                                                    style=wx.DEFAULT_FRAME_STYLE | wx.FRAME_FLOAT_ON_PARENT)

                    preview_frame.Initialize()

                    preview_canvas = preview.GetCanvas()
                    preview_canvas.SetMinSize(preview_canvas.GetVirtualSize())
                    preview_frame.Fit()

                    preview_frame.Show(True)
        else:
            selected = self.ActiveChild
            if selected and isinstance(selected.Children[0], Viewer):
                window = selected.Children[0]
                data = wx.PrintDialogData(self.PrintData)
                properties = self.Controler.GetProjectProperties(window.IsDebugging())
                page_size = list(map(int, properties["pageSize"]))
                margins = (self.PageSetupData.GetMarginTopLeft(), self.PageSetupData.GetMarginBottomRight())
                printout = GraphicPrintout(window, page_size, margins, True)
                printout2 = GraphicPrintout(window, page_size, margins, True)
                preview = wx.PrintPreview(printout, printout2, data)

                if preview.IsOk():
                    preview_frame = wx.PreviewFrame(preview, self, _("Print preview"),
                                                    style=wx.DEFAULT_FRAME_STYLE | wx.FRAME_FLOAT_ON_PARENT)
                    preview_frame.Initialize()
                    preview_canvas = preview.GetCanvas()
                    preview_canvas.SetMinSize(preview_canvas.GetVirtualSize())
                    preview_frame.Fit()
                    preview_frame.Show(True)

    def OnPrintMenu(self, event):
        if USETAB:
            selected = self.TabsOpened.GetSelection()
            if selected != -1:
                window = self.TabsOpened.GetPage(selected)
                dialog_data = wx.PrintDialogData(self.PrintData)
                dialog_data.SetToPage(1)
                properties = self.Controler.GetProjectProperties(window.IsDebugging())
                page_size = map(int, properties["pageSize"])
                margins = (self.PageSetupData.GetMarginTopLeft(), self.PageSetupData.GetMarginBottomRight())
                printer = wx.Printer(dialog_data)
                printout = GraphicPrintout(window, page_size, margins)

                if not printer.Print(self, printout, True) and printer.GetLastError() != wx.PRINTER_CANCELLED:
                    self.ShowErrorMessage(
                        _("There was a problem printing.\nPerhaps your current printer is not set correctly?"))
                printout.Destroy()
        else:
            selected = self.ActiveChild
            if selected and isinstance(selected.Children[0], Viewer):
                window = selected.Children[0]
                dialog_data = wx.PrintDialogData(self.PrintData)
                dialog_data.SetToPage(1)
                properties = self.Controler.GetProjectProperties(window.IsDebugging())
                page_size = list(map(int, properties["pageSize"]))
                margins = (self.PageSetupData.GetMarginTopLeft(), self.PageSetupData.GetMarginBottomRight())
                printer = wx.Printer(dialog_data)
                printout = GraphicPrintout(window, page_size, margins)
                if not printer.Print(self, printout, True) and printer.GetLastError() != wx.PRINTER_CANCELLED:
                    self.ShowErrorMessage(
                        _("There was a problem printing.\nPerhaps your current printer is not set correctly?"))
                printout.Destroy()

    def OnPropertiesMenu(self, event):
        self.EditProjectSettings()

    def OnQuitMenu(self, event):
        self.Close()

    def OnCloseFrame(self, event):
        self.AUIManager.UnInit()

    # -------------------------------------------------------------------------------
    #                            Edit Menu Functions
    # -------------------------------------------------------------------------------

    def RefreshEditMenu(self):
        MenuToolBar = self.Panes["MenuToolBar"]
        if self.Controler is not None:
            if USETAB:
                selected = self.TabsOpened.GetSelection()
                if selected > -1:
                    window = self.TabsOpened.GetPage(selected)
                    undo, redo = window.GetBufferState()
                else:
                    undo, redo = self.Controler.GetBufferState()
                self.EditMenu.Enable(wx.ID_UNDO, undo)
                MenuToolBar.EnableTool(wx.ID_UNDO, undo)
                self.EditMenu.Enable(wx.ID_REDO, redo)
                MenuToolBar.EnableTool(wx.ID_REDO, redo)
                # self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO, True)
                # self.EditMenu.Check(ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO,
                #                self.Controler.IsProjectBufferEnabled())
                self.EditMenu.Enable(wx.ID_FIND, selected > -1)
                self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUFINDNEXT,
                                     selected > -1 and self.SearchParams is not None)
                self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUFINDPREVIOUS,
                                     selected > -1 and self.SearchParams is not None)
                self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, True)
                MenuToolBar.EnableTool(ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, True)
                self.EditMenu.Enable(wx.ID_ADD, True)
                self.EditMenu.Enable(wx.ID_DELETE, True)
                if self.TabsOpened.GetPageCount() > 0:
                    self.EditMenu.Enable(wx.ID_CUT, True)
                    MenuToolBar.EnableTool(wx.ID_CUT, True)
                    self.EditMenu.Enable(wx.ID_COPY, True)
                    MenuToolBar.EnableTool(wx.ID_COPY, True)
                    if self.GetCopyBuffer() is not None:
                        self.EditMenu.Enable(wx.ID_PASTE, True)
                        MenuToolBar.EnableTool(wx.ID_PASTE, True)
                    else:
                        self.EditMenu.Enable(wx.ID_PASTE, False)
                        MenuToolBar.EnableTool(wx.ID_PASTE, False)
                    self.EditMenu.Enable(wx.ID_SELECTALL, True)
                else:
                    self.EditMenu.Enable(wx.ID_CUT, False)
                    MenuToolBar.EnableTool(wx.ID_CUT, False)
                    self.EditMenu.Enable(wx.ID_COPY, False)
                    MenuToolBar.EnableTool(wx.ID_COPY, False)
                    self.EditMenu.Enable(wx.ID_PASTE, False)
                    MenuToolBar.EnableTool(wx.ID_PASTE, False)
                    self.EditMenu.Enable(wx.ID_SELECTALL, False)
            else:
                selected = self.ActiveChild or False
                if selected and isinstance(selected.Children[0], Viewer):
                    try:
                        window = selected.Children[0]
                        undo, redo = window.GetBufferState()
                    except Exception as ex:
                        logger.debug(traceback.format_exc())
                        undo, redo = False, False
                else:
                    undo, redo = self.Controler.GetBufferState()
                self.EditMenu.Enable(wx.ID_UNDO, undo)
                MenuToolBar.EnableTool(wx.ID_UNDO, undo)
                self.EditMenu.Enable(wx.ID_REDO, redo)
                MenuToolBar.EnableTool(wx.ID_REDO, redo)
                self.EditMenu.Enable(wx.ID_FIND, isinstance(selected, wx.MDIChildFrame))
                self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUFINDNEXT,
                                     isinstance(selected, wx.MDIChildFrame) and self.SearchParams is not None)
                self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUFINDPREVIOUS,
                                     isinstance(selected, wx.MDIChildFrame) and self.SearchParams is not None)
                self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, True)
                MenuToolBar.EnableTool(ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, True)
                self.EditMenu.Enable(wx.ID_ADD, True)
                self.EditMenu.Enable(wx.ID_DELETE, True)
                self.EditMenu.Enable(wx.ID_FLOPPY, True)
                if len(self.ChildWindow()) > 0:
                    self.EditMenu.Enable(wx.ID_CUT, True)
                    MenuToolBar.EnableTool(wx.ID_CUT, True)
                    self.EditMenu.Enable(wx.ID_COPY, True)
                    MenuToolBar.EnableTool(wx.ID_COPY, True)
                    if self.GetCopyBuffer() is not None:
                        self.EditMenu.Enable(wx.ID_PASTE, True)
                        MenuToolBar.EnableTool(wx.ID_PASTE, True)
                    else:
                        self.EditMenu.Enable(wx.ID_PASTE, False)
                        MenuToolBar.EnableTool(wx.ID_PASTE, False)
                    self.EditMenu.Enable(wx.ID_SELECTALL, True)
                else:
                    self.EditMenu.Enable(wx.ID_CUT, False)
                    MenuToolBar.EnableTool(wx.ID_CUT, False)
                    self.EditMenu.Enable(wx.ID_COPY, False)
                    MenuToolBar.EnableTool(wx.ID_COPY, False)
                    self.EditMenu.Enable(wx.ID_PASTE, False)
                    MenuToolBar.EnableTool(wx.ID_PASTE, False)
                    self.EditMenu.Enable(wx.ID_SELECTALL, False)
        else:
            self.EditMenu.Enable(wx.ID_UNDO, False)
            MenuToolBar.EnableTool(wx.ID_UNDO, False)
            self.EditMenu.Enable(wx.ID_REDO, False)
            MenuToolBar.EnableTool(wx.ID_REDO, False)
            # self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO, False)
            self.EditMenu.Enable(wx.ID_CUT, False)
            MenuToolBar.EnableTool(wx.ID_CUT, False)
            self.EditMenu.Enable(wx.ID_COPY, False)
            MenuToolBar.EnableTool(wx.ID_COPY, False)
            self.EditMenu.Enable(wx.ID_PASTE, False)
            MenuToolBar.EnableTool(wx.ID_PASTE, False)
            self.EditMenu.Enable(wx.ID_SELECTALL, False)
            self.EditMenu.Enable(wx.ID_FIND, False)
            self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUFINDNEXT, False)
            self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUFINDPREVIOUS, False)
            self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, False)
            MenuToolBar.EnableTool(ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, False)
            self.EditMenu.Enable(wx.ID_ADD, False)
            self.EditMenu.Enable(wx.ID_DELETE, False)
            self.EditMenu.Enable(wx.ID_FLOPPY, False)

    def CloseTabsWithoutModel(self, refresh=True):
        if USETAB:
            idxs = range(self.TabsOpened.GetPageCount())
            idxs.reverse()
            for idx in idxs:
                window = self.TabsOpened.GetPage(idx)
                if window.HasNoModel():
                    self.TabsOpened.DeletePage(idx)
        else:
            for idx in self.ChildWindow():
                window = idx.Children[0]
                if not isinstance(window, Viewer) and window.HasNoModel():
                    idx.Close()
        if refresh:
            self.RefreshEditor()

    def OnUndoMenu(self, event):
        selected = self.ActiveChild
        if selected and isinstance(selected.Children[0], Viewer):
            window = selected.Children[0]
            window.Undo()
        else:
            if self.Controler:
                self.Controler.LoadPrevious()
        self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE,
                      SCALING, PAGETITLES)

    def OnRedoMenu(self, event):
        selected = self.ActiveChild
        if selected and isinstance(selected.Children[0], Viewer):
            window = selected.Children[0]
            window.Redo()
        else:
            if self.Controler:
                self.Controler.LoadNext()
        self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE,
                      SCALING, PAGETITLES)

    def OnEnableUndoRedoMenu(self, event):
        self.Controler.EnableProjectBuffer(event.IsChecked())
        self.RefreshEditMenu()

    OnCutMenu = GetShortcutKeyCallbackFunction("Cut")
    OnCopyMenu = GetShortcutKeyCallbackFunction("Copy")
    OnPasteMenu = GetShortcutKeyCallbackFunction("Paste")

    def OnSelectAllMenu(self, event):
        control = self.FindFocus()
        if control is not None and control.GetName() == "Viewer":
            control.Parent.SelectAll()
        elif isinstance(control, wx.stc.StyledTextCtrl):
            control.SelectAll()
        elif isinstance(control, wx.TextCtrl):
            control.SetSelection(0, control.GetLastPosition())
        elif isinstance(control, wx.ComboBox):
            control.SetMark(0, control.GetLastPosition() + 1)

    def SetDeleteFunctions(self):
        self.DeleteFunctions = {
            ITEM_DATATYPE: GetDeleteElementFunction(
                PLCControler.ProjectRemoveDataType,
                check_function=self.CheckDataTypeIsUsedBeforeDeletion),
            ITEM_POU: GetDeleteElementFunction(
                PLCControler.ProjectRemovePou,
                check_function=self.CheckPouIsUsedBeforeDeletion),
            ITEM_TRANSITION: GetDeleteElementFunction(
                PLCControler.ProjectRemovePouTransition, ITEM_POU),
            ITEM_ACTION: GetDeleteElementFunction(
                PLCControler.ProjectRemovePouAction, ITEM_POU),
            ITEM_CONFIGURATION: GetDeleteElementFunction(
                PLCControler.ProjectRemoveConfiguration),
            ITEM_RESOURCE: GetDeleteElementFunction(
                PLCControler.ProjectRemoveConfigurationResource, ITEM_CONFIGURATION)
        }

    def OnDeleteMenu(self, event):
        '''\@ entry 删除'''
        window = self.FindFocus()
        if window == self.ProjectTree or window is None:
            selected = self.ProjectTree.GetSelection()
            if selected is not None and selected.IsOk():
                function = self.DeleteFunctions.get(self.ProjectTree.GetItemData(selected)["type"], None)
                if function is not None:
                    function(self, selected)
                    self.CloseTabsWithoutModel(refresh=False)
                    self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, PROJECTTREE,
                                  POUINSTANCEVARIABLESPANEL, LIBRARYTREE)
        elif isinstance(window, (Viewer, TextViewer)):
            ievent = wx.KeyEvent(wx.EVT_CHAR._getEvtType())
            ievent.m_keyCode = wx.WXK_DELETE
            window.ProcessEvent(ievent)

    def findObject(self, paths, test):
        path = None
        for p in paths:
            if test(p):
                path = p
                break
        return path

    def OnPlatformUpdate(self, event):
        import sys  # 首部导入会被优化掉
        if not self.Controler: return
        PrjPath = self.Controler.GetProjectPath()
        logger.info('pio_update:%s' % PrjPath)
        old_path = os.getcwd()
        os.chdir(PrjPath)
        if exists('platformio.ini'):
            logger.info('pio_update: run')
            cmd = 'platformio.exe'
            app_path = self.Controler.GetIECLibPath()
            self.app_path = os.path.abspath(os.path.join(app_path, '../../..', ))
            application_path = os.path.dirname(sys.executable)
            pio_paths = [
                os.path.join(self.app_path, 'Python36', 'Scripts'),
                os.path.join(application_path, 'Scripts')
            ]
            pio = self.findObject(
                pio_paths, lambda p: os.path.isfile(os.path.join(p, cmd)))
            cmdd = "%s/%s update" % (pio.replace('\\', '/'), cmd)
            cmdd = cmdd.replace('/', '\\')
            self.Controler.logger.info(cmdd)
            output_encoding = locale.getdefaultlocale()[1]
            status, _result, _err_result = ProcessLogger(self.Controler.logger, cmdd, no_gui=True,
                                                         output_encoding='utf-8').spin()
            if status:
                self.Controler.logger.error(_("platform update  failed.\n"))
            else:
                self.Controler.logger.info(_("platform update successed..\n"))
        else:
            logger.info('not found platformio.ini.')
        os.chdir(old_path)
        logger.info('pio_update: end')

    def OnFindMenu(self, event):
        if not self.FindDialog.IsShown():
            self.FindDialog.Show()
            self.FindDialog.FindPattern.SetFocus()

    def CloseFindInPouDialog(self):
        if USETAB:
            selected = self.TabsOpened.GetSelection()
            if selected == -1 and self.FindDialog.IsShown():
                self.FindDialog.Hide()
        else:
            selected = self.ActiveChild
            if selected and self.FindDialog.IsShown():
                self.FindDialog.Hide()

    def OnFindNextMenu(self, event):
        self.FindInPou(1)

    def OnFindPreviousMenu(self, event):
        self.FindInPou(-1)

    def FindInPou(self, direction, search_params=None):
        if search_params is not None:
            self.SearchParams = search_params
            if USETAB:
                selected = self.TabsOpened.GetSelection()
                if selected != -1:
                    window = self.TabsOpened.GetPage(selected)
                    window.Find(direction, self.SearchParams)
            else:
                selected = self.ActiveChild
                if selected:
                    window = selected.Children[0]
                    window.Find(direction, self.SearchParams)

    def OnSearchInProjectMenu(self, event):
        dialog = SearchInProjectDialog(self)
        if dialog.ShowModal() == wx.ID_OK:
            criteria = dialog.GetCriteria()
            if len(criteria) > 0:
                result = self.Controler.SearchInProject(criteria)
                self.ClearSearchResults()
                self.SearchResultPanel.SetSearchResults(criteria, result)
                self.SelectTab(self.SearchResultPanel)

    # -------------------------------------------------------------------------------
    #                             Display Menu Functions
    # -------------------------------------------------------------------------------

    def RefreshDisplayMenu(self):
        if self.Controler is not None:
            if USETAB:
                if self.TabsOpened.GetPageCount() > 0:
                    self.DisplayMenu.Enable(wx.ID_REFRESH, True)
                    selected = self.TabsOpened.GetSelection()
                    if selected != -1:
                        window = self.TabsOpened.GetPage(selected)
                        if isinstance(window, Viewer):
                            self.DisplayMenu.Enable(wx.ID_ZOOM_FIT, True)
                            zoommenu = self.DisplayMenu.FindItemById(wx.ID_ZOOM_FIT).GetSubMenu()
                            zoomitem = zoommenu.FindItemByPosition(window.GetScale())
                            zoomitem.Check(True)
                        else:
                            self.DisplayMenu.Enable(wx.ID_ZOOM_FIT, False)
                    else:
                        self.DisplayMenu.Enable(wx.ID_ZOOM_FIT, False)
                else:
                    self.DisplayMenu.Enable(wx.ID_REFRESH, False)
                    self.DisplayMenu.Enable(wx.ID_ZOOM_FIT, False)
            else:
                if len(self.ChildWindow()) > 0:
                    self.DisplayMenu.Enable(wx.ID_REFRESH, True)
                    selected = self.ActiveChild
                    if selected and isinstance(selected.Children[0], Viewer):
                        window = selected.Children[0]
                        if isinstance(window, Viewer):
                            self.DisplayMenu.Enable(wx.ID_ZOOM_FIT, True)
                            zoommenu = self.DisplayMenu.FindItemById(wx.ID_ZOOM_FIT).GetSubMenu()
                            zoomitem = zoommenu.FindItemByPosition(window.GetScale())
                            zoomitem.Check(True)
                        else:
                            self.DisplayMenu.Enable(wx.ID_ZOOM_FIT, False)
                    else:
                        self.DisplayMenu.Enable(wx.ID_ZOOM_FIT, False)
                else:
                    self.DisplayMenu.Enable(wx.ID_REFRESH, False)
                    self.DisplayMenu.Enable(wx.ID_ZOOM_FIT, False)
        if self.EnableDebug:
            self.DisplayMenu.Enable(wx.ID_CLEAR, True)
        else:
            self.DisplayMenu.Enable(wx.ID_REFRESH, False)
            if self.EnableDebug:
                self.DisplayMenu.Enable(wx.ID_CLEAR, False)
            self.DisplayMenu.Enable(wx.ID_ZOOM_FIT, False)

    def OnRefreshMenu(self, event):
        self.RefreshEditor()

    def OnClearErrorsMenu(self, event):
        self.ClearErrors()

    def GenerateZoomFunction(self, idx):
        def ZoomFunction(event):
            if USETAB:
                selected = self.TabsOpened.GetSelection()
                if selected != -1:
                    window = self.TabsOpened.GetPage(selected)
                    window.SetScale(idx)
                    window.RefreshVisibleElements()
                    window.RefreshScrollBars()
            else:
                selected = self.ActiveChild
                if selected and isinstance(selected.Children[0], Viewer):
                    window = selected.Children[0]
                    window.SetScale(idx)
                    window.RefreshVisibleElements()
                    window.RefreshScrollBars()
            event.Skip()

        return ZoomFunction

    def OnResetPerspective(self, event):
        self.ResetPerspective()

    # -------------------------------------------------------------------------------
    #                      Project Editor Panels Management Functions
    # -------------------------------------------------------------------------------

    def OnPageDragged(self, event):
        wx.CallAfter(self.RefreshTabCtrlEvent)
        event.Skip()

    def OnAllowNotebookDnD(self, event):
        event.Allow()

    def RefreshTabCtrlEvent(self):
        auitabctrl = []
        if USETAB:
            for child in self.TabsOpened.GetChildren():
                if isinstance(child, wx.aui.AuiTabCtrl):
                    auitabctrl.append(child)
                    if child not in self.AuiTabCtrl:
                        child.Bind(wx.EVT_LEFT_DCLICK, self.GetTabsOpenedDClickFunction(child))
            self.AuiTabCtrl = auitabctrl
            if self.TabsOpened.GetPageCount() == 0:
                pane = self.AUIManager.GetPane(self.TabsOpened)
                self.AUIManager.Update()
        else:
            for child in self.ChildWindow():
                if isinstance(child, wx.aui.AuiTabCtrl):
                    auitabctrl.append(child)
                    if child not in self.AuiTabCtrl:
                        child.Bind(wx.EVT_LEFT_DCLICK, self.GetTabsOpenedDClickFunction(child))
            self.AuiTabCtrl = auitabctrl
            if len(self.ChildWindow()) == 0:
                #     self.AUIManager.RestorePane(pane)
                self.AUIManager.Update()

    def EnsureTabVisible(self, tab):
        notebook = tab.GetParent()
        notebook.SetSelection(notebook.GetPageIndex(tab))

    def OnPouSelectedChanging(self, event):
        if USETAB:
            selected = self.TabsOpened.GetSelection()
            if selected >= 0:
                window = self.TabsOpened.GetPage(selected)
                if not window.IsDebugging():
                    window.ResetBuffer()
        else:
            selected = self.ActiveChild
            if selected and isinstance(selected.Children[0], Viewer):
                window = selected.Children[0]
                if not window.IsDebugging():
                    window.ResetBuffer()
        event.Skip()

    def OnPouSelectedChanged(self, event):
        '''
        ##@event EVT_ACTIVATE
        ##@BRIEF i跟随窗口切换刷新调试变量面板
        :param event:
        :return:
        '''
        if USETAB:
            selected = self.TabsOpened.GetSelection()
            if selected >= 0:
                window = self.TabsOpened.GetPage(selected)
                tagname = window.GetTagName()
                if not window.IsDebugging():
                    self.SelectProjectTreeItem(tagname)
                    self.PouInstanceVariablesPanel.SetPouType(tagname)
                    window.RefreshView()
                    self.EnsureTabVisible(self.LibraryPanel)
                else:
                    instance_path = window.GetInstancePath()
                    if tagname == "":
                        instance_path = instance_path.rsplit(".", 1)[0]
                        tagname = self.Controler.GetPouInstanceTagName(instance_path, self.EnableDebug)
                    self.EnsureTabVisible(self.DebugVariablePanel)
                    wx.CallAfter(self.PouInstanceVariablesPanel.SetPouType, tagname, instance_path)
            wx.CallAfter(self._Refresh, FILEMENU, EDITMENU, DISPLAYMENU, EDITORTOOLBAR)
        else:
            if event.Active:
                try:
                    selected = self.ActiveChild
                    if selected and isinstance(selected.Children[0], Viewer):
                        window = selected.Children[0]
                        tagname = window.GetTagName()
                        if not window.IsDebugging():
                            self.SelectProjectTreeItem(tagname)
                            self.PouInstanceVariablesPanel.SetPouType(tagname)
                            window.RefreshView()
                            self.EnsureTabVisible(self.LibraryPanel)
                        else:
                            instance_path = window.GetInstancePath()
                            if tagname == "":
                                instance_path = instance_path.rsplit(".", 1)[0]
                                tagname = self.Controler.GetPouInstanceTagName(instance_path, self.EnableDebug)
                            self.EnsureTabVisible(self.DebugVariablePanel)
                            wx.CallAfter(self.PouInstanceVariablesPanel.SetPouType, tagname, instance_path)
                    wx.CallAfter(self._Refresh, FILEMENU, EDITMENU, DISPLAYMENU, EDITORTOOLBAR)
                except Exception as ex:
                    logger.debug(traceback.format_exc())
        event.Skip()

    def RefreshEditor(self):
        if USETAB:
            selected = self.TabsOpened.GetSelection()
            if selected >= 0:
                window = self.TabsOpened.GetPage(selected)
                tagname = window.GetTagName()
                if not window.IsDebugging():
                    self.SelectProjectTreeItem(tagname)
                    self.PouInstanceVariablesPanel.SetPouType(tagname)
                else:
                    instance_path = window.GetInstancePath()
                    if tagname == "":
                        instance_path = instance_path.rsplit(".", 1)[0]
                        tagname = self.Controler.GetPouInstanceTagName(instance_path, self.EnableDebug)
                    self.PouInstanceVariablesPanel.SetPouType(tagname, instance_path)
                for child in self.TabsOpened.GetChildren():
                    if isinstance(child, wx.aui.AuiTabCtrl):
                        active_page = child.GetActivePage()
                        if active_page >= 0:
                            window = child.GetWindowFromIdx(active_page)
                            window.RefreshView()
                self._Refresh(FILEMENU, EDITMENU, DISPLAYMENU, EDITORTOOLBAR)
        else:
            if self.ActiveChild and isinstance(self.ActiveChild, Viewer):
                window = self.ActiveChild.Children[0]
                tagname = window.GetTagName()
                if not window.IsDebugging():
                    self.SelectProjectTreeItem(tagname)
                    self.PouInstanceVariablesPanel.SetPouType(tagname)
                else:
                    instance_path = window.GetInstancePath()
                    if tagname == "":
                        instance_path = instance_path.rsplit(".", 1)[0]
                        tagname = self.Controler.GetPouInstanceTagName(instance_path, self.EnableDebug)
                    self.PouInstanceVariablesPanel.SetPouType(tagname, instance_path)
                for child in self.ChildWindow():
                    window = child.Children[0]
                    window.RefreshView()
                self._Refresh(FILEMENU, EDITMENU, DISPLAYMENU, EDITORTOOLBAR)

    def RefreshEditorNames(self, old_tagname, new_tagname):
        if USETAB:
            for i in range(self.TabsOpened.GetPageCount()):
                editor = self.TabsOpened.GetPage(i)
                if editor.GetTagName() == old_tagname:
                    editor.SetTagName(new_tagname)
        else:
            for i in self.ChildWindow():
                editor = i
                if isinstance(i.Children[0], Viewer) and editor.Children[0].GetTagName() == old_tagname:
                    editor.Children[0].SetTagName(new_tagname)

    def IsOpened_tab(self, tagname):
        for idx in range(self.TabsOpened.GetPageCount()):
            if self.TabsOpened.GetPage(idx).IsViewing(tagname):
                return idx
        return None

    def ChildWindow(self):
        ws = []
        for idx in self.Children:
            if isinstance(idx, wx.MDIChildFrame):
                ws.append(idx)
        return ws

    def IsOpened(self, tagname):
        for idx in self.ChildWindow():
            if idx.Children[0].IsViewing(tagname):
                return idx
        if USETAB:
            return self.IsOpened_tab(tagname)
        return None

    def RefreshPageTitles(self):
        if USETAB:
            for idx in range(self.TabsOpened.GetPageCount()):
                window = self.TabsOpened.GetPage(idx)
                icon = window.GetIcon()
                if icon is not None:
                    self.SetPageBitmap(idx, icon)
                self.TabsOpened.SetPageText(idx, window.GetTitle())
        else:
            for idx in self.ChildWindow():
                window = idx
                icon = window.GetIcon()
                if icon is not None:
                    self.SetPageBitmap(idx, icon)

    def GetTabsOpenedDClickFunction(self, tabctrl):
        def OnTabsOpenedDClick(event):
            pos = event.GetPosition()
            if tabctrl.TabHitTest(pos.x, pos.y):
                self.SwitchPerspective(event)
            event.Skip()

        return OnTabsOpenedDClick

    def SwitchPerspective(self, evt):
        # pane = self.AUIManager.GetPane(self.TabsOpened)
        # on wxPython 4.1.0, AuiPaneInfo has no "IsMaximized" attribute...
        # if not pane.IsResizable():
        #     pane.Movable()
        # else:
        #     pane.Dock()
        self.AUIManager.Update()

    def SwitchFullScrMode(self, evt):
        show = not self.IsFullScreen()
        self.ShowFullScreen(show)

    # -------------------------------------------------------------------------------
    #                         Types Tree Management Functions
    # -------------------------------------------------------------------------------

    def RefreshProjectTree(self):
        # Extract current selected item tagname
        selected = self.ProjectTree.GetSelection()
        if selected:
            item_infos = self.ProjectTree.GetItemData(selected)
            tagname = item_infos.get("tagname", None)
        else:
            tagname = None

        # Refresh treectrl items according to project infos
        if self.Controler:
            infos = self.Controler.GetProjectInfos()
            root = self.ProjectTree.GetRootItem()
            if root is None or not root.IsOk():
                root = self.ProjectTree.AddRoot(infos["name"])
            self.GenerateProjectTreeBranch(root, infos)
            self.ProjectTree.Expand(root)

        # Select new item corresponding to previous selected item
        if tagname is not None:
            self.SelectProjectTreeItem(tagname)

    def GenerateProjectTreeBranch(self, root, infos, item_alone=False):
        to_delete = []
        item_name = infos["name"]
        if infos["type"] in ITEMS_UNEDITABLE:
            if len(infos["values"]) == 1:
                return self.GenerateProjectTreeBranch(root, infos["values"][0], True)
            item_name = _(item_name)
        self.ProjectTree.SetItemText(root, item_name)
        self.ProjectTree.SetItemData(root, infos)
        highlight_colours = self.Highlights.get(infos.get("tagname", None), (wx.Colour(255, 255, 255, 0), wx.BLACK))
        self.ProjectTree.SetItemBackgroundColour(root, highlight_colours[0])
        self.ProjectTree.SetItemTextColour(root, highlight_colours[1])
        self.ProjectTree.SetItemExtraImage(root, None)
        if infos["type"] == ITEM_POU:
            self.ProjectTree.SetItemImage(
                root, self.TreeImageDict[self.Controler.GetPouBodyType(infos["name"])])
            if item_alone:
                self.ProjectTree.SetItemExtraImage(root, self.Controler.GetPouType(infos["name"]))
        elif "icon" in infos and infos["icon"] is not None:
            icon_name = infos["icon"]
            if icon_name not in self.TreeImageDict:
                self.TreeImageDict[icon_name] = self.TreeImageList.Add(GetBitmap(icon_name))
            self.ProjectTree.SetItemImage(root, self.TreeImageDict[icon_name])
        elif infos["type"] in self.TreeImageDict:
            self.ProjectTree.SetItemImage(root, self.TreeImageDict[infos["type"]])

        item, root_cookie = self.ProjectTree.GetFirstChild(root)
        for values in infos["values"]:
            if values["type"] not in ITEMS_UNEDITABLE or len(values["values"]) > 0:
                if item is None or not item.IsOk():
                    item = self.ProjectTree.AppendItem(root, "")
                    item, root_cookie = self.ProjectTree.GetNextChild(root, root_cookie)
                self.GenerateProjectTreeBranch(item, values)
                item, root_cookie = self.ProjectTree.GetNextChild(root, root_cookie)
        while item is not None and item.IsOk():
            to_delete.append(item)
            item, root_cookie = self.ProjectTree.GetNextChild(root, root_cookie)
        for item in to_delete:
            self.ProjectTree.Delete(item)

    TagNamePartsItemTypes = {
        "D": [ITEM_DATATYPE],
        "P": [ITEM_POU],
        "T": [ITEM_POU, ITEM_TRANSITION],
        "A": [ITEM_POU, ITEM_ACTION],
        "C": [ITEM_CONFIGURATION],
        "R": [ITEM_CONFIGURATION, ITEM_RESOURCE]}

    def SelectProjectTreeItem(self, tagname):
        result = False
        if self.ProjectTree is not None:
            root = self.ProjectTree.GetRootItem()
            if root is not None and root.IsOk():
                words = tagname.split("::")
                result = self.RecursiveProjectTreeItemSelection(
                    root, list(zip(words[1:], self.TagNamePartsItemTypes.get(words[0], []))))
        return result

    def RecursiveProjectTreeItemSelection(self, root, items):
        found = False
        item, root_cookie = self.ProjectTree.GetFirstChild(root)
        while item is not None and item.IsOk() and not found:
            item_infos = self.ProjectTree.GetItemData(item)
            if (item_infos["name"].split(":")[-1].strip(), item_infos["type"]) == items[0]:
                if len(items) == 1:
                    self.SelectedItem = item
                    self.ProjectTree.SelectItem(item)
                    self.ResetSelectedItem()
                    return True
                else:
                    found = self.RecursiveProjectTreeItemSelection(item, items[1:])
            else:
                found = self.RecursiveProjectTreeItemSelection(item, items)
            item, root_cookie = self.ProjectTree.GetNextChild(root, root_cookie)
        return found

    def ResetSelectedItem(self):
        self.SelectedItem = None

    def OnProjectTreeBeginDrag(self, event):
        selected_item = (self.SelectedItem
                         if self.SelectedItem is not None
                         else event.GetItem())
        if selected_item.IsOk() and self.ProjectTree.GetItemData(selected_item)["type"] == ITEM_POU:
            block_name = self.ProjectTree.GetItemText(selected_item)
            block_type = self.Controler.GetPouType(block_name)
            if block_type != "program":
                data = wx.TextDataObject(str((block_name, block_type, "")))
                dragSource = wx.DropSource(self.ProjectTree)
                dragSource.SetData(data)
                dragSource.DoDragDrop()
            self.ResetSelectedItem()

    def OnProjectTreeItemBeginEdit(self, event):
        selected = event.GetItem()
        if self.ProjectTree.GetItemData(selected)["type"] in ITEMS_UNEDITABLE:
            event.Veto()
        else:
            event.Skip()

    def OnProjectTreeItemEndEdit(self, event):
        message = None
        abort = False
        new_name = event.GetLabel()
        if new_name.upper() in reversed_word:
            message = _('"%s" is reversed word!') % new_name
            abort = True
        if new_name != "":
            if not TestIdentifier(new_name):
                message = _("\"%s\" is not a valid identifier!") % new_name
            elif new_name.upper() in IEC_KEYWORDS:
                message = _("\"%s\" is a keyword. It can't be used!") % new_name
            else:
                item = event.GetItem()
                old_name = self.ProjectTree.GetItemText(item)
                item_infos = self.ProjectTree.GetItemData(item)
                if item_infos["type"] == ITEM_PROJECT:
                    self.Controler.SetProjectProperties(name=new_name)
                elif item_infos["type"] == ITEM_DATATYPE:
                    if new_name.upper() in [name.upper() for name in self.Controler.GetProjectDataTypeNames() if
                                            name != old_name]:
                        message = _("\"%s\" data type already exists!") % new_name
                        abort = True
                    if not abort:
                        self.Controler.ChangeDataTypeName(old_name, new_name)
                        self.RefreshEditorNames(ComputeDataTypeName(old_name),
                                                ComputeDataTypeName(new_name))
                        self.RefreshPageTitles()
                elif item_infos["type"] == ITEM_POU:
                    if new_name.upper() in [name.upper() for name in
                                            self.Controler.GetProjectPouNames() if
                                            name != old_name]:
                        message = _("\"%s\" pou already exists!") % new_name
                        abort = True
                    elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouVariableNames()]:
                        messageDialog = wx.MessageDialog(self, _(
                            "A POU has an element named \"%s\". This could cause a conflict. Do you wish to continue?") % new_name,
                                                         _("Error"), wx.YES_NO | wx.ICON_QUESTION)
                        if messageDialog.ShowModal() == wx.ID_NO:
                            abort = True
                        messageDialog.Destroy()
                    if not abort:
                        self.Controler.ChangePouName(old_name, new_name)
                        self.RefreshEditorNames(ComputePouName(old_name),
                                                ComputePouName(new_name))
                        self.RefreshLibraryPanel()
                        self.RefreshPageTitles()
                elif item_infos["type"] == ITEM_TRANSITION:
                    pou_item = self.ProjectTree.GetItemParent(event.GetItem())
                    pou_name = self.ProjectTree.GetItemText(pou_item)
                    if new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames()]:
                        message = _("A POU named \"%s\" already exists!") % new_name
                    elif new_name.upper() in [name.upper() for name in
                                              self.Controler.GetProjectPouVariableNames(pou_name) if name != old_name]:
                        message = _("A variable with \"%s\" as name already exists in this pou!") % new_name
                    else:
                        words = item_infos["tagname"].split("::")
                        self.Controler.ChangePouTransitionName(words[1], old_name, new_name)
                        self.RefreshEditorNames(ComputePouTransitionName(words[1], old_name),
                                                ComputePouTransitionName(words[1], new_name))
                        self.RefreshPageTitles()
                elif item_infos["type"] == ITEM_ACTION:
                    pou_item = self.ProjectTree.GetItemParent(event.GetItem())
                    pou_name = self.ProjectTree.GetItemText(pou_item)
                    if new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames()]:
                        message = _("A POU named \"%s\" already exists!") % new_name
                    elif new_name.upper() in [name.upper() for name in
                                              self.Controler.GetProjectPouVariableNames(pou_name) if name != old_name]:
                        message = _("A variable with \"%s\" as name already exists in this pou!") % new_name
                    else:
                        words = item_infos["tagname"].split("::")
                        self.Controler.ChangePouActionName(words[1], old_name, new_name)
                        self.RefreshEditorNames(ComputePouActionName(words[1], old_name),
                                                ComputePouActionName(words[1], new_name))
                        self.RefreshPageTitles()
                elif item_infos["type"] == ITEM_CONFIGURATION:
                    if new_name.upper() in [name.upper() for name in self.Controler.GetProjectConfigNames() if
                                            name != old_name]:
                        message = _("\"%s\" config already exists!") % new_name
                        abort = True
                    elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames()]:
                        messageDialog = wx.MessageDialog(self, _(
                            "There is a POU named \"%s\". This could cause a conflict. Do you wish to continue?") % new_name,
                                                         _("Error"), wx.YES_NO | wx.ICON_QUESTION)
                        if messageDialog.ShowModal() == wx.ID_NO:
                            abort = True
                        messageDialog.Destroy()
                    elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouVariableNames()]:
                        messageDialog = wx.MessageDialog(self, _(
                            "A POU has an element named \"%s\". This could cause a conflict. Do you wish to continue?") % new_name,
                                                         _("Error"), wx.YES_NO | wx.ICON_QUESTION)
                        if messageDialog.ShowModal() == wx.ID_NO:
                            abort = True
                        messageDialog.Destroy()
                    if not abort:
                        self.Controler.ChangeConfigurationName(old_name, new_name)
                        self.RefreshEditorNames(ComputeConfigurationName(old_name),
                                                ComputeConfigurationName(new_name))
                        self.RefreshPageTitles()
                elif item_infos["type"] == ITEM_RESOURCE:
                    if new_name.upper() in [name.upper() for name in self.Controler.GetProjectConfigNames()]:
                        message = _("\"%s\" config already exists!") % new_name
                        abort = True
                    elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames()]:
                        messageDialog = wx.MessageDialog(self, _(
                            "There is a POU named \"%s\". This could cause a conflict. Do you wish to continue?") % new_name,
                                                         _("Error"), wx.YES_NO | wx.ICON_QUESTION)
                        if messageDialog.ShowModal() == wx.ID_NO:
                            abort = True
                        messageDialog.Destroy()
                    elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouVariableNames()]:
                        messageDialog = wx.MessageDialog(self, _(
                            "A POU has an element named \"%s\". This could cause a conflict. Do you wish to continue?") % new_name,
                                                         _("Error"), wx.YES_NO | wx.ICON_QUESTION)
                        if messageDialog.ShowModal() == wx.ID_NO:
                            abort = True
                        messageDialog.Destroy()
                    if not abort:
                        words = item_infos["tagname"].split("::")
                        self.Controler.ChangeConfigurationResourceName(words[1], old_name, new_name)
                        self.RefreshEditorNames(ComputeConfigurationResourceName(words[1], old_name),
                                                ComputeConfigurationResourceName(words[1], new_name))
                        self.RefreshPageTitles()
            if message or abort:
                if message:
                    self.ShowErrorMessage(message)
                event.Veto()
            else:
                wx.CallAfter(self.RefreshProjectTree)
                self.RefreshEditor()
                self._Refresh(TITLE, FILEMENU, EDITMENU)
                event.Skip()

    def OnProjectTreeItemActivated(self, event):
        selected = event.GetItem()
        item_infos = self.ProjectTree.GetItemData(selected)
        if item_infos["type"] == ITEM_PROJECT:
            self.EditProjectSettings()
        else:
            if item_infos["type"] in [ITEM_DATATYPE, ITEM_POU,
                                      ITEM_CONFIGURATION, ITEM_RESOURCE,
                                      ITEM_TRANSITION, ITEM_ACTION]:
                self.EditProjectElement(item_infos["type"], item_infos["tagname"])
            event.Skip()

    def ProjectTreeItemSelect(self, select_item):
        if select_item is not None and select_item.IsOk():
            item_infos = self.ProjectTree.GetItemData(select_item)
            if item_infos["type"] in [ITEM_DATATYPE, ITEM_POU,
                                      ITEM_CONFIGURATION, ITEM_RESOURCE,
                                      ITEM_TRANSITION, ITEM_ACTION]:
                self.EditProjectElement(item_infos["type"], item_infos["tagname"], True)
                self.PouInstanceVariablesPanel.SetPouType(item_infos["tagname"])

    def OnProjectTreeLeftUp(self, event):
        '''\@ entry 项目树左键'''
        if self.SelectedItem is not None:
            self.ProjectTree.SelectItem(self.SelectedItem)
            self.ProjectTreeItemSelect(self.SelectedItem)
            self.ResetSelectedItem()
        event.Skip()

    def OnProjectTreeMotion(self, event):
        if not event.Dragging():
            pt = wx.Point(event.GetX(), event.GetY())
            item, flags = self.ProjectTree.HitTest(pt)
            if item is not None and item.IsOk() and flags & wx.TREE_HITTEST_ONITEMLABEL:
                item_infos = self.ProjectTree.GetItemData(item)
                if item != self.LastToolTipItem and self.LastToolTipItem is not None:
                    self.ProjectTree.SetToolTip(None)
                    self.LastToolTipItem = None
                if self.LastToolTipItem != item and \
                        item_infos["type"] in [ITEM_POU, ITEM_TRANSITION, ITEM_ACTION]:
                    bodytype = self.Controler.GetEditedElementBodyType(
                        item_infos["tagname"])
                    if item_infos["type"] == ITEM_POU:
                        block_type = {
                            "program": _("Program"),
                            "functionBlock": _("Function Block"),
                            "function": _("Function")
                        }[self.Controler.GetPouType(item_infos["name"])]
                    elif item_infos["type"] == ITEM_TRANSITION:
                        block_type = "Transition"
                    else:
                        block_type = "Action"
                    self.LastToolTipItem = item
                    wx.CallAfter(self.ProjectTree.SetToolTip,
                                 "%s : %s : %s" % (
                                     block_type, bodytype, item_infos["name"]))
            elif self.LastToolTipItem is not None:
                self.ProjectTree.SetToolTip(None)
                self.LastToolTipItem = None
        event.Skip()

    def OnProjectTreeItemChanging(self, event):
        if self.ProjectTree.GetItemData(event.GetItem())["type"] not in ITEMS_UNEDITABLE and self.SelectedItem is None:
            self.SelectedItem = event.GetItem()
            event.Veto()
        else:
            event.Skip()

    def GetProjectElementWindow(self, element, tagname):
        new_window = None
        if self.Controler.GetEditedElement(tagname) is not None:
            new_window = None
            if USETAB:
                win = self.TabsOpened
            else:
                win = wx.MDIChildFrame(self, -1, tagname)
            if element == ITEM_CONFIGURATION:
                new_window = ConfigurationEditor(win, tagname, self, self.Controler)
                new_window.SetIcon(GetBitmap("CONFIGURATION"))
            elif element == ITEM_RESOURCE:
                new_window = ResourceEditor(win, tagname, self, self.Controler)
                new_window.SetIcon(GetBitmap("RESOURCE"))
            elif element in [ITEM_POU, ITEM_TRANSITION, ITEM_ACTION]:
                bodytype = self.Controler.GetEditedElementBodyType(tagname)
                if bodytype == "FBD":
                    new_window = Viewer(win, tagname, self, self.Controler)
                    new_window.RefreshScaling(False)
                elif bodytype == "LD":
                    new_window = LD_Viewer(win, tagname, self, self.Controler)
                    new_window.RefreshScaling(False)
                elif bodytype == "SFC":
                    new_window = SFC_Viewer(win, tagname, self, self.Controler)
                    new_window.RefreshScaling(False)
                else:
                    new_window = TextViewer(win, tagname, self, self.Controler)
                    new_window.SetTextSyntax(bodytype)
                    if bodytype == "IL":
                        new_window.SetKeywords(IL_KEYWORDS)
                    else:
                        new_window.SetKeywords(ST_KEYWORDS)
                if element == ITEM_POU:
                    pou_type = self.Controler.GetEditedElementType(tagname)[1].upper()
                    icon = GetBitmap(pou_type, bodytype)
                elif element == ITEM_TRANSITION:
                    icon = GetBitmap("TRANSITION", bodytype)
                elif element == ITEM_ACTION:
                    icon = GetBitmap("ACTION", bodytype)
                new_window.SetIcon(icon)
            elif element == ITEM_DATATYPE:
                new_window = DataTypeEditor(win, tagname, self, self.Controler)
                new_window.SetIcon(GetBitmap("DATATYPE"))
            if not USETAB:
                new_window.RefreshView()
                # win.Bind(wx.EVT_ACTIVATE, self.OnPouSelectedChanged)
                win.Bind(wx.EVT_CLOSE, new_window.onClose)
                win.Maximize()
                win.Show(True)

        return new_window

    def EditProjectElement(self, element, tagname, onlyopened=False):
        openedidx = self.IsOpened(tagname)
        if openedidx is not None:
            if USETAB:
                old_selected = self.TabsOpened.GetSelection()
                if old_selected != openedidx:
                    if old_selected >= 0:
                        self.TabsOpened.GetPage(old_selected).ResetBuffer()
                    self.TabsOpened.SetSelection(openedidx)
            else:
                openedidx.Activate()
                self._Refresh(FILEMENU, EDITMENU, EDITORTOOLBAR, PAGETITLES)

        elif not onlyopened:
            if USETAB:
                if isinstance(element, EditorPanel):
                    new_window = element
                else:
                    new_window = self.GetProjectElementWindow(element, tagname)
                if new_window is not None:
                    self.AddPage(new_window, "")
                    openedidx = self.IsOpened(tagname)
                    old_selected = self.TabsOpened.GetSelection()
                    if old_selected != openedidx and old_selected >= 0:
                        self.TabsOpened.GetPage(old_selected).ResetBuffer()
                    for i in range(self.TabsOpened.GetPageCount()):
                        window = self.TabsOpened.GetPage(i)
                        if window == new_window:
                            self.TabsOpened.SetSelection(i)
                            window.SetFocus()
                self.RefreshPageTitles()
            else:
                new_window = self.GetProjectElementWindow(element, tagname)
            return new_window

    def OnProjectTreeRightUp(self, event):
        '''\@ entry 项目树右键菜单'''
        item = event.GetItem()
        self.ProjectTree.SelectItem(item)
        self.ProjectTreeItemSelect(item)
        name = self.ProjectTree.GetItemText(item)
        item_infos = self.ProjectTree.GetItemData(item)

        menu = None
        if item_infos["type"] in list(ITEMS_UNEDITABLE) + [ITEM_PROJECT]:
            if item_infos["type"] == ITEM_PROJECT:
                name = "Project"
            else:
                name = self.UNEDITABLE_NAMES_DICT[name]

            if name == "Data Types":
                menu = wx.Menu(title='')
                new_id = wx.NewIdRef()
                AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add DataType"))
                self.Bind(wx.EVT_MENU, self.OnAddDataTypeMenu, id=new_id)

            elif name in ["Functions", "Function Blocks", "Programs", "Project"]:
                menu = wx.Menu(title='')

                if name != "Project":
                    new_id = wx.NewIdRef()
                    AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add POU"))
                    self.Bind(wx.EVT_MENU, self.GenerateAddPouFunction(
                        {"Functions": "function", "Function Blocks": "functionBlock", "Programs": "program"}[name]),
                              id=new_id)

                new_id = wx.NewIdRef()
                AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Paste POU"))
                self.Bind(wx.EVT_MENU, self.OnPastePou, id=new_id)
                if self.GetCopyBuffer() is None:
                    menu.Enable(new_id, False)

            elif name == "Configurations":
                menu = wx.Menu(title='')
                new_id = wx.NewIdRef()
                AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add Configuration"))
                self.Bind(wx.EVT_MENU, self.OnAddConfigurationMenu, id=new_id)

            elif name == "Transitions":
                menu = wx.Menu(title='')
                new_id = wx.NewIdRef()
                AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add Transition"))
                parent = self.ProjectTree.GetItemParent(item)
                parent_type = self.ProjectTree.GetItemData(parent)["type"]
                while parent_type != ITEM_POU:
                    parent = self.ProjectTree.GetItemParent(parent)
                    parent_type = self.ProjectTree.GetItemData(parent)["type"]
                self.Bind(wx.EVT_MENU, self.GenerateAddTransitionFunction(self.ProjectTree.GetItemText(parent)),
                          id=new_id)

            elif name == "Actions":
                menu = wx.Menu(title='')
                new_id = wx.NewIdRef()
                AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add Action"))
                parent = self.ProjectTree.GetItemParent(item)
                parent_type = self.ProjectTree.GetItemData(parent)["type"]
                while parent_type != ITEM_POU:
                    parent = self.ProjectTree.GetItemParent(parent)
                    parent_type = self.ProjectTree.GetItemData(parent)["type"]
                self.Bind(wx.EVT_MENU, self.GenerateAddActionFunction(self.ProjectTree.GetItemText(parent)), id=new_id)

            elif name == "Resources":
                menu = wx.Menu(title='')
                new_id = wx.NewIdRef()
                AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add Resource"))
                parent = self.ProjectTree.GetItemParent(item)
                parent_type = self.ProjectTree.GetItemData(parent)["type"]
                while parent_type not in [ITEM_CONFIGURATION, ITEM_PROJECT]:
                    parent = self.ProjectTree.GetItemParent(parent)
                    parent_type = self.ProjectTree.GetItemData(parent)["type"]
                parent_name = None
                if parent_type == ITEM_PROJECT:
                    config_names = self.Controler.GetProjectConfigNames()
                    if len(config_names) > 0:
                        parent_name = config_names[0]
                else:
                    parent_name = self.ProjectTree.GetItemText(parent)
                if parent_name is not None:
                    self.Bind(wx.EVT_MENU, self.GenerateAddResourceFunction(parent_name), id=new_id)

        else:
            if item_infos["type"] == ITEM_POU:
                menu = wx.Menu(title='')
                if self.Controler.GetPouBodyType(name) == "SFC":
                    new_id = wx.NewIdRef()
                    AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add Transition"))
                    self.Bind(wx.EVT_MENU, self.GenerateAddTransitionFunction(name), id=new_id)
                    new_id = wx.NewIdRef()
                    AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add Action"))
                    self.Bind(wx.EVT_MENU, self.GenerateAddActionFunction(name), id=new_id)
                    menu.AppendSeparator()

                new_id = wx.NewIdRef()
                AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Copy POU"))
                self.Bind(wx.EVT_MENU, self.OnCopyPou, id=new_id)

                pou_type = self.Controler.GetPouType(name)
                if pou_type in ["function", "functionBlock"]:
                    change_menu = wx.Menu(title='')
                    if pou_type == "function":
                        new_id = wx.NewIdRef()
                        AppendMenu(change_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Function Block"))
                        self.Bind(wx.EVT_MENU, self.GenerateChangePouTypeFunction(name, "functionBlock"), id=new_id)
                    new_id = wx.NewIdRef()
                    AppendMenu(change_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Program"))
                    self.Bind(wx.EVT_MENU, self.GenerateChangePouTypeFunction(name, "program"), id=new_id)
                    menu.AppendMenu(wx.NewId(), _("Change POU Type To"), change_menu)
                new_id = wx.NewIdRef()
                AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Rename"))
                self.Bind(wx.EVT_MENU, self.OnRenamePouMenu, id=new_id)

            elif item_infos["type"] == ITEM_CONFIGURATION:
                menu = wx.Menu(title='')
                new_id = wx.NewIdRef()
                AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add Resource"))
                self.Bind(wx.EVT_MENU, self.GenerateAddResourceFunction(name), id=new_id)

            elif item_infos["type"] in [ITEM_DATATYPE, ITEM_TRANSITION, ITEM_ACTION, ITEM_RESOURCE]:
                menu = wx.Menu(title='')

            if menu is not None:
                new_id = wx.NewIdRef()
                AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Delete"))
                self.Bind(wx.EVT_MENU, self.OnDeleteMenu, id=new_id)

        if menu is not None:
            self.FindFocus().PopupMenu(menu)
            menu.Destroy()

        self.ResetSelectedItem()

        event.Skip()

    # -------------------------------------------------------------------------------
    #                         Instances Tree Management Functions
    # -------------------------------------------------------------------------------

    def GetTreeImage(self, var_class):
        return self.TreeImageDict.get(var_class)

    def RefreshPouInstanceVariablesPanel(self):
        self.PouInstanceVariablesPanel.RefreshView()

    def OpenDebugViewer_(self, instance_category, instance_path, instance_type):
        openedidx = self.IsOpened(instance_path)
        new_window = None
        if openedidx is not None:
            old_selected = self.TabsOpened.GetSelection()
            if old_selected != openedidx:
                if old_selected >= 0:
                    self.TabsOpened.GetPage(old_selected).ResetBuffer()
                self.TabsOpened.SetSelection(openedidx)

        elif instance_category in ITEMS_VARIABLE:
            if self.Controler.IsNumType(instance_type, True):
                self.AddDebugVariable(instance_path, True)

        else:
            bodytype = self.Controler.GetEditedElementBodyType(instance_type, True)
            if bodytype == "FBD":
                new_window = Viewer(self.TabsOpened, instance_type, self, self.Controler, True, instance_path)
                new_window.RefreshScaling(False)
            elif bodytype == "LD":
                new_window = LD_Viewer(self.TabsOpened, instance_type, self, self.Controler, True, instance_path)
                new_window.RefreshScaling(False)
            elif bodytype == "SFC":
                new_window = SFC_Viewer(self.TabsOpened, instance_type, self, self.Controler, True, instance_path)
                new_window.RefreshScaling(False)
            else:
                new_window = TextViewer(self.TabsOpened, instance_type, self, self.Controler, True, instance_path)
                new_window.SetTextSyntax(bodytype)
                if bodytype == "IL":
                    new_window.SetKeywords(IL_KEYWORDS)
                else:
                    new_window.SetKeywords(ST_KEYWORDS)

            if new_window is not None:
                if instance_category in [ITEM_FUNCTIONBLOCK, ITEM_PROGRAM]:
                    pou_type = self.Controler.GetEditedElementType(instance_type, True)[1].upper()
                    icon = GetBitmap(pou_type, bodytype)
                elif instance_category == ITEM_TRANSITION:
                    icon = GetBitmap("TRANSITION", bodytype)
                elif instance_category == ITEM_ACTION:
                    icon = GetBitmap("ACTION", bodytype)

        if new_window is not None:
            new_window.SetIcon(icon)
            self.AddPage(new_window, "")
            new_window.RefreshView()
            new_window.SetFocus()
            self.RefreshPageTitles()
        return new_window

    def OpenDebugViewer(self, instance_category, instance_path, instance_type):
        '''打开监控窗口'''
        openedidx = self.IsOpened(instance_type)
        new_window = None
        if openedidx is not None and isinstance(openedidx.Children[0], Viewer):
            window = openedidx
            if window.Children[0].Debug:
                window.Children[0].setDebug(None)
            else:
                window.Children[0].setDebug(instance_path)
            old_selected = self.ActiveChild
            if old_selected != openedidx:
                if old_selected:
                    old_selected.Children[0].ResetBuffer()
                ## @ todo :没有切换后台窗口到前台
                openedidx.Show(True)

        elif instance_category in ITEMS_VARIABLE:
            if self.Controler.IsNumType(instance_type, True):
                self.AddDebugVariable(instance_path, False)
        else:
            bodytype = self.Controler.GetEditedElementBodyType(instance_type, True)
            win = wx.MDIChildFrame(self, -1, instance_type)
            if bodytype == "FBD":
                new_window = Viewer(win, instance_type, self, self.Controler, True, instance_path)
                new_window.RefreshScaling(False)
            elif bodytype == "LD":
                new_window = LD_Viewer(win, instance_type, self, self.Controler, True, instance_path)
                new_window.RefreshScaling(False)
            elif bodytype == "SFC":
                new_window = SFC_Viewer(win, instance_type, self, self.Controler, True, instance_path)
                new_window.RefreshScaling(False)
            else:
                new_window = TextViewer(win, instance_type, self, self.Controler, True, instance_path)
                new_window.SetTextSyntax(bodytype)
                if bodytype == "IL":
                    new_window.SetKeywords(IL_KEYWORDS)
                else:
                    new_window.SetKeywords(ST_KEYWORDS)
            # win.Bind(wx.EVT_ACTIVATE, self.OnPouSelectedChanged)
            # win.Bind(wx.EVT_CLOSE, new_window.onClose)
            win.Maximize()
            win.Show(True)
            if new_window is not None:
                if instance_category in [ITEM_FUNCTIONBLOCK, ITEM_PROGRAM]:
                    pou_type = self.Controler.GetEditedElementType(instance_type, True)[1].upper()
                    icon = GetBitmap(pou_type, bodytype)
                elif instance_category == ITEM_TRANSITION:
                    icon = GetBitmap("TRANSITION", bodytype)
                elif instance_category == ITEM_ACTION:
                    icon = GetBitmap("ACTION", bodytype)

        if new_window is not None:
            new_window.SetIcon(icon)
            self.AddPage(new_window, "")
            new_window.RefreshView()
            new_window.setDebug(instance_path)
            new_window.SetFocus()
            self.RefreshPageTitles()
        return new_window

    def ResetGraphicViewers(self):
        if self.EnableDebug:
            self.DebugVariablePanel.ResetGraphicsValues()

    def CloseObsoleteDebugTabs(self):
        if self.EnableDebug:
            if USETAB:
                idxs = range(self.TabsOpened.GetPageCount())
                idxs.reverse()
                for idx in idxs:
                    editor = self.TabsOpened.GetPage(idx)
                    if isinstance(editor, Viewer) and editor.IsDebugging():
                        instance_infos = self.Controler.GetInstanceInfos(editor.GetInstancePath(), self.EnableDebug)
                        if instance_infos is None:
                            self.TabsOpened.DeletePage(idx)
                        else:
                            editor.SubscribeAllDataConsumers()
                    elif editor.IsDebugging() and hasattr(editor, 'SubscribeAllDataConsumers'):
                        editor.SubscribeAllDataConsumers()
            else:
                for idx in self.ChildWindow():
                    editor = idx.Children[0]
                    if isinstance(editor, Viewer) and editor.IsDebugging():
                        instance_infos = self.Controler.GetInstanceInfos(editor.GetInstancePath(), self.EnableDebug)
                        if instance_infos is None:
                            idx.Close()
                        else:
                            editor.SubscribeAllDataConsumers()
                    elif editor.IsDebugging() and hasattr(editor, 'SubscribeAllDataConsumers'):
                        editor.SubscribeAllDataConsumers()

            self.DebugVariablePanel.SubscribeAllDataConsumers()

    def AddDebugVariable(self, iec_path, force=False, graph=False):
        if self.EnableDebug:
            self.DebugVariablePanel.InsertValue(iec_path, force=force, graph=graph)
            self.EnsureTabVisible(self.DebugVariablePanel)

    # -------------------------------------------------------------------------------
    #                         Library Panel Management Function
    # -------------------------------------------------------------------------------

    def RefreshLibraryPanel(self):
        self.LibraryPanel.RefreshTree()

    # -------------------------------------------------------------------------------
    #                          ToolBars Management Functions
    # -------------------------------------------------------------------------------

    def AddToMenuToolBar(self, items):
        MenuToolBar = self.Panes["MenuToolBar"]
        if MenuToolBar.GetToolsCount() > 0:
            MenuToolBar.AddSeparator()
        for toolbar_item in items:
            if toolbar_item is None:
                MenuToolBar.AddSeparator()
            else:
                iid, bitmap, ihelp, callback = toolbar_item
                MenuToolBar.AddTool(toolId=iid, label='tppl', shortHelp=ihelp, bitmap=GetBitmap(bitmap))
                if callback is not None:
                    self.Bind(wx.EVT_TOOL, callback, id=iid)
        MenuToolBar.Realize()
        self.AUIManager.GetPane("MenuToolBar").BestSize(MenuToolBar.GetBestSize())

    def ResetEditorToolBar(self):
        EditorToolBar = self.Panes["EditorToolBar"]

        for item in self.CurrentEditorToolBar:
            self.Unbind(wx.EVT_MENU, id=item)

            if EditorToolBar:
                EditorToolBar.DeleteTool(item)

        if EditorToolBar:
            EditorToolBar.Realize()
            self.AUIManager.GetPane("EditorToolBar").BestSize(EditorToolBar.GetBestSize())
            self.AUIManager.GetPane("EditorToolBar").Hide()
            self.AUIManager.Update()

    def RefreshEditorToolBar(self):
        menu = None
        if USETAB:
            selected = self.TabsOpened.GetSelection()
            menu = None
            if selected != -1:
                window = self.TabsOpened.GetPage(selected)
                if isinstance(window, (Viewer, TextViewer)):
                    if not window.IsDebugging():
                        menu = self.Controler.GetEditedElementBodyType(window.GetTagName())
                    else:
                        menu = "debug"
        else:
            if self.ActiveChild and self.ActiveChild.Children and isinstance(self.ActiveChild.Children[0], Viewer):
                window = self.ActiveChild.Children[0]
                if isinstance(window, (Viewer, TextViewer)):
                    if not window.IsDebugging() and self.Controler:
                        menu = self.Controler.GetEditedElementBodyType(window.GetTagName())
                    else:
                        menu = "debug"

        if menu is not None and menu != self.CurrentMenu:
            self.ResetEditorToolBar()
            self.CurrentMenu = menu
            self.CurrentEditorToolBar = []
            EditorToolBar = self.Panes["EditorToolBar"]
            if EditorToolBar:
                for radio, modes, id, method, picture, help in self.EditorToolBarItems[menu]:
                    if modes & self.DrawingMode:
                        if radio or self.DrawingMode == FREEDRAWING_MODE:
                            EditorToolBar.AddRadioTool(id, 'label', GetBitmap(picture), wx.NullBitmap, help)
                        else:
                            EditorToolBar.AddTool(id, GetBitmap(picture), help)
                        self.Bind(wx.EVT_MENU, getattr(self, method), id=id)
                        self.CurrentEditorToolBar.append(id)
                EditorToolBar.Realize()
                self.AUIManager.GetPane("EditorToolBar").Show()
                self.AUIManager.Update()
                self.AUIManager.GetPane("EditorToolBar").BestSize(EditorToolBar.GetBestSize())
                self.AUIManager.Update()
        elif menu is None:
            self.ResetEditorToolBar()
            self.CurrentMenu = menu
        self.ResetCurrentMode()

    # -------------------------------------------------------------------------------
    #                           EditorToolBar Items Functions
    # -------------------------------------------------------------------------------

    def ResetCurrentMode(self):
        if USETAB:
            selected = self.TabsOpened.GetSelection()
            if selected != -1:
                window = self.TabsOpened.GetPage(selected)
                window.SetMode(MODE_SELECTION)
        else:
            selected = self.ActiveChild
            if selected and selected.Children and isinstance(selected.Children[0], Viewer):
                window = selected
                window.Children[0].SetMode(MODE_SELECTION)

        EditorToolBar = self.Panes["EditorToolBar"]
        if EditorToolBar:
            EditorToolBar.ToggleTool(ID_PLCOPENEDITOREDITORTOOLBARSELECTION, False)
            EditorToolBar.ToggleTool(ID_PLCOPENEDITOREDITORTOOLBARSELECTION, True)

    def ResetToolToggle(self, id):
        tool = self.Panes["EditorToolBar"].FindById(id)
        tool.SetToggle(False)

    def OnSelectionTool(self, event):
        selected = self.ActiveChild
        if selected and isinstance(selected.Children[0], Viewer):
            selected.Children[0].SetMode(MODE_SELECTION)

    def OnMotionTool(self, event):
        if USETAB:
            selected = self.TabsOpened.GetSelection()
            if selected != -1:
                self.TabsOpened.GetPage(selected).SetMode(MODE_SELECTION)
        else:
            selected = self.ActiveChild
            if selected and isinstance(selected.Children[0], Viewer):
                selected.Children[0].SetMode(MODE_MOTION)

    def OnCommentTool(self, event):
        self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARCOMMENT)
        if USETAB:
            selected = self.TabsOpened.GetSelection()
            if selected != -1:
                self.TabsOpened.GetPage(selected).SetMode(MODE_COMMENT)
        else:
            selected = self.ActiveChild
            if selected and isinstance(selected.Children[0], Viewer):
                selected.Children[0].SetMode(MODE_COMMENT)

    def OnVariableTool(self, event):
        self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARVARIABLE)
        if USETAB:
            selected = self.TabsOpened.GetSelection()
            if selected != -1:
                self.TabsOpened.GetPage(selected).SetMode(MODE_VARIABLE)
        else:
            selected = self.ActiveChild
            if selected and isinstance(selected.Children[0], Viewer):
                selected.Children[0].SetMode(MODE_VARIABLE)

    def OnBlockTool(self, event):
        self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARBLOCK)
        if USETAB:
            selected = self.TabsOpened.GetSelection()
            if selected != -1:
                self.TabsOpened.GetPage(selected).SetMode(MODE_BLOCK)
        else:
            selected = self.ActiveChild
            if selected and isinstance(selected.Children[0], Viewer):
                selected.Children[0].SetMode(MODE_BLOCK)

    def OnConnectionTool(self, event):
        self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARCONNECTION)
        if USETAB:
            selected = self.TabsOpened.GetSelection()
            if selected != -1:
                self.TabsOpened.GetPage(selected).SetMode(MODE_CONNECTION)
        else:
            selected = self.ActiveChild
            if selected and isinstance(selected.Children[0], Viewer):
                selected.Children[0].SetMode(MODE_CONNECTION)

    def OnPowerRailTool(self, event):
        self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARPOWERRAIL)
        if USETAB:
            selected = self.TabsOpened.GetSelection()
            if selected != -1:
                self.TabsOpened.GetPage(selected).SetMode(MODE_POWERRAIL)
        else:
            selected = self.ActiveChild
            if selected and isinstance(selected.Children[0], Viewer):
                selected.Children[0].SetMode(MODE_POWERRAIL)

    def OnRungTool(self, event):
        if USETAB:
            selected = self.TabsOpened.GetSelection()
            if selected != -1:
                self.TabsOpened.GetPage(selected).AddLadderRung()
        else:
            selected = self.ActiveChild
            if selected and isinstance(selected.Children[0], Viewer):
                selected.Children[0].saveVari()
                selected.Children[0].AddLadderRung()

        event.Skip()

    def OnCoilTool(self, event):
        self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARCOIL)
        if USETAB:
            selected = self.TabsOpened.GetSelection()
            if selected != -1:
                self.TabsOpened.GetPage(selected).SetMode(MODE_COIL)
        else:
            selected = self.ActiveChild
            if selected and isinstance(selected.Children[0], Viewer):
                selected.Children[0].SetMode(MODE_COIL)

        event.Skip()

    def OnContactTool(self, event):
        if self.DrawingMode == FREEDRAWING_MODE:
            self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARCONTACT)
            if USETAB:
                selected = self.TabsOpened.GetSelection()
                if selected != -1:
                    if self.DrawingMode == FREEDRAWING_MODE:
                        self.TabsOpened.GetPage(selected).SetMode(MODE_CONTACT)
                    else:
                        self.TabsOpened.GetPage(selected).AddLadderContact()
            else:
                selected = self.ActiveChild
                if selected and isinstance(selected.Children[0], Viewer):
                    if self.DrawingMode == FREEDRAWING_MODE:
                        selected.Children[0].SetMode(MODE_CONTACT)
                    else:
                        selected.Children[0].saveVari()
                        selected.Children[0].AddLadderContact()

    def OnBranchTool(self, event):
        if USETAB:
            selected = self.TabsOpened.GetSelection()
            if selected != -1:
                self.TabsOpened.GetPage(selected).AddLadderBranch()
        else:
            selected = self.ActiveChild
            if selected and isinstance(selected.Children[0], Viewer):
                selected.Children[0].saveVari()
                selected.Children[0].AddLadderBranch()

    def OnInitialStepTool(self, event):
        self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARINITIALSTEP)
        if USETAB:
            selected = self.TabsOpened.GetSelection()
            if selected != -1:
                self.TabsOpened.GetPage(selected).SetMode(MODE_INITIALSTEP)
        else:
            selected = self.ActiveChild
            if selected and isinstance(selected.Children[0], Viewer):
                selected.Children[0].SetMode(MODE_INITIALSTEP)

    def OnStepTool(self, event):
        if self.GetDrawingMode() == FREEDRAWING_MODE:
            self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARSTEP)
            if USETAB:
                selected = self.TabsOpened.GetSelection()
                if selected != -1:
                    if self.GetDrawingMode() == FREEDRAWING_MODE:
                        self.TabsOpened.GetPage(selected).SetMode(MODE_STEP)
                    else:
                        self.TabsOpened.GetPage(selected).AddStep()
            else:
                selected = self.ActiveChild
                if selected and isinstance(selected.Children[0], Viewer):
                    if self.GetDrawingMode() == FREEDRAWING_MODE:
                        selected.Children[0].SetMode(MODE_STEP)
                    else:
                        selected.Children[0].saveVari()
                        selected.Children[0].AddStep()

    def OnActionBlockTool(self, event):
        if self.GetDrawingMode() == FREEDRAWING_MODE:
            self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARACTIONBLOCK)
            if USETAB:
                selected = self.TabsOpened.GetSelection()
                if selected != -1:
                    if self.GetDrawingMode() == FREEDRAWING_MODE:
                        self.TabsOpened.GetPage(selected).SetMode(MODE_ACTION)
                    else:
                        self.TabsOpened.GetPage(selected).AddStepAction()
            else:
                selected = self.ActiveChild
                if selected and isinstance(selected.Children[0], Viewer):
                    if self.GetDrawingMode() == FREEDRAWING_MODE:
                        selected.Children[0].SetMode(MODE_ACTION)
                    else:
                        selected.Children[0].saveVari()
                        selected.Children[0].AddStepAction()

    def OnTransitionTool(self, event):
        self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARTRANSITION)
        if USETAB:
            selected = self.TabsOpened.GetSelection()
            if selected != -1:
                self.TabsOpened.GetPage(selected).SetMode(MODE_TRANSITION)
        else:
            selected = self.ActiveChild
            if selected and isinstance(selected.Children[0], Viewer):
                selected.Children[0].SetMode(MODE_TRANSITION)

    def OnDivergenceTool(self, event):
        if self.GetDrawingMode() == FREEDRAWING_MODE:
            self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARDIVERGENCE)
            if USETAB:
                selected = self.TabsOpened.GetSelection()
                if selected != -1:
                    if self.GetDrawingMode() == FREEDRAWING_MODE:
                        self.TabsOpened.GetPage(selected).SetMode(MODE_DIVERGENCE)
                    else:
                        self.TabsOpened.GetPage(selected).AddDivergence()
            else:
                selected = self.ActiveChild
                if selected and isinstance(selected.Children[0], Viewer):
                    if self.GetDrawingMode() == FREEDRAWING_MODE:
                        selected.Children[0].SetMode(MODE_DIVERGENCE)
                    else:
                        selected.Children[0].saveVari()
                        selected.Children[0].AddDivergence()

    def OnJumpTool(self, event):
        if self.GetDrawingMode() == FREEDRAWING_MODE:
            self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARJUMP)
            if USETAB:
                selected = self.TabsOpened.GetSelection()
                if selected != -1:
                    if self.GetDrawingMode() == FREEDRAWING_MODE:
                        self.TabsOpened.GetPage(selected).SetMode(MODE_JUMP)
                    else:
                        self.TabsOpened.GetPage(selected).AddJump()
            else:
                selected = self.ActiveChild
                if selected and isinstance(selected.Children[0], Viewer):
                    if self.GetDrawingMode() == FREEDRAWING_MODE:
                        selected.Children[0].SetMode(MODE_JUMP)
                    else:
                        selected.Children[0].saveVari()
                        selected.Children[0].AddJump()

    # -------------------------------------------------------------------------------
    #                         Add Project Elements Functions
    # -------------------------------------------------------------------------------

    def OnAddNewProject(self, event):
        # Asks user to create main program after creating new project
        AddProgramDialog = self.GenerateAddPouFunction('program', True)
        # Checks that user created main program
        if AddProgramDialog(event):
            self.Controler.SetProjectDefaultConfiguration()

    def OnAddDataTypeMenu(self, event):
        tagname = self.Controler.ProjectAddDataType()
        if tagname is not None:
            self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE)
            self.EditProjectElement(ITEM_DATATYPE, tagname)

    def GenerateAddPouFunction(self, pou_type, type_readonly=False):
        def OnAddPouMenu(event):
            ##@function:添加POU
            try:
                dialog = PouDialog(self, pou_type, type_readonly)
                dialog.SetPouNames(self.Controler.GetProjectPouNames())
                dialog.SetPouElementNames(self.Controler.GetProjectPouVariableNames())  ## 这个应该没有用,界面上没有显示
                dialog.SetValues({"pouName": self.Controler.GenerateNewName(None, None, "%s%%d" % pou_type)})
                pou_created = False
                if dialog.ShowModal() == wx.ID_OK:
                    values = dialog.GetValues()
                    tagname = self.Controler.ProjectAddPou(values["pouName"], values["pouType"], values["language"])
                    if tagname is not None:
                        self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE, LIBRARYTREE)
                        self.EditProjectElement(ITEM_POU, tagname)
                        dialog.Destroy()
                        pou_created = True
                dialog.Destroy()
                return pou_created
            except Exception as ex:
                logger.error(traceback.format_exc())
                self.ShowMessage(str(ex))

        return OnAddPouMenu

    def GenerateAddTransitionFunction(self, pou_name):
        def OnAddTransitionMenu(event):
            dialog = PouTransitionDialog(self)
            dialog.SetPouNames(self.Controler.GetProjectPouNames())
            dialog.SetPouElementNames(self.Controler.GetProjectPouVariableNames(pou_name))
            dialog.SetValues({"transitionName": self.Controler.GenerateNewName(None, None, "transition%d")})
            if dialog.ShowModal() == wx.ID_OK:
                values = dialog.GetValues()
                tagname = self.Controler.ProjectAddPouTransition(pou_name, values["transitionName"], values["language"])
                if tagname is not None:
                    self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE)
                    self.EditProjectElement(ITEM_TRANSITION, tagname)
            dialog.Destroy()

        return OnAddTransitionMenu

    def GenerateAddActionFunction(self, pou_name):
        def OnAddActionMenu(event):
            dialog = PouActionDialog(self)
            dialog.SetPouNames(self.Controler.GetProjectPouNames())
            dialog.SetPouElementNames(self.Controler.GetProjectPouVariableNames(pou_name))
            dialog.SetValues({"actionName": self.Controler.GenerateNewName(None, None, "action%d")})
            if dialog.ShowModal() == wx.ID_OK:
                values = dialog.GetValues()
                tagname = self.Controler.ProjectAddPouAction(pou_name, values["actionName"], values["language"])
                if tagname is not None:
                    self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE)
                    self.EditProjectElement(ITEM_ACTION, tagname)
            dialog.Destroy()

        return OnAddActionMenu

    def OnAddConfigurationMenu(self, event):
        tagname = self.Controler.ProjectAddConfiguration()
        if tagname is not None:
            self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL)
            self.EditProjectElement(ITEM_CONFIGURATION, tagname)

    def AddResourceMenu(self, event):
        config_names = self.Controler.GetProjectConfigNames()
        if len(config_names) > 0:
            tagname = self.Controler.ProjectAddConfigurationResource(config_names[0])
            if tagname is not None:
                self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL)
                self.EditProjectElement(ITEM_RESOURCE, tagname)

    def GenerateAddResourceFunction(self, config_name):
        def OnAddResourceMenu(event):
            tagname = self.Controler.ProjectAddConfigurationResource(config_name)
            if tagname is not None:
                self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL)
                self.EditProjectElement(ITEM_RESOURCE, tagname)

        return OnAddResourceMenu

    def GenerateChangePouTypeFunction(self, name, new_type):
        def OnChangePouTypeMenu(event):
            selected = self.ProjectTree.GetSelection()
            if self.ProjectTree.GetItemData(selected)["type"] == ITEM_POU:
                self.Controler.ProjectChangePouType(name, new_type)
                self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, PROJECTTREE, LIBRARYTREE)

        return OnChangePouTypeMenu

    def OnCopyPou(self, event):
        selected = self.ProjectTree.GetSelection()
        pou_name = self.ProjectTree.GetItemText(selected)

        pou_xml = self.Controler.GetPouXml(pou_name)
        if pou_xml is not None:
            self.SetCopyBuffer(pou_xml)
            self._Refresh(EDITMENU)

    def OnPastePou(self, event):
        selected = self.ProjectTree.GetSelection()

        if self.ProjectTree.GetItemData(selected)["type"] != ITEM_PROJECT:
            pou_type = self.ProjectTree.GetItemText(selected)
            pou_type = self.UNEDITABLE_NAMES_DICT[pou_type]  # one of 'Functions', 'Function Blocks' or 'Programs'
            pou_type = {'Functions': 'function', 'Function Blocks': 'functionBlock', 'Programs': 'program'}[pou_type]
        else:
            pou_type = None

        pou_xml = self.GetCopyBuffer()

        result = self.Controler.PastePou(pou_type, pou_xml)

        if not isinstance(result, tuple):
            self.ShowErrorMessage(result)
        else:
            self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, PROJECTTREE, LIBRARYTREE)
            self.EditProjectElement(ITEM_POU, result[0])

    # -------------------------------------------------------------------------------
    #                        Remove Project Elements Functions
    # -------------------------------------------------------------------------------

    def CheckElementIsUsedBeforeDeletion(self, check_function, title, name):
        if not check_function(name):
            return True

        dialog = wx.MessageDialog(
            self,
            _("\"%s\" is used by one or more POUs. Do you wish to continue?") % name,
            title, wx.YES_NO | wx.ICON_QUESTION)
        answer = dialog.ShowModal()
        dialog.Destroy()
        return answer == wx.ID_YES

    def CheckDataTypeIsUsedBeforeDeletion(self, name):
        return self.CheckElementIsUsedBeforeDeletion(
            self.Controler.DataTypeIsUsed,
            _("Remove Datatype"), name)

    def CheckPouIsUsedBeforeDeletion(self, name):
        return self.CheckElementIsUsedBeforeDeletion(
            self.Controler.PouIsUsed,
            _("Remove Pou"), name)

    def OnRemoveDataTypeMenu(self, event):
        selected = self.ProjectTree.GetSelection()
        if self.ProjectTree.GetItemData(selected)["type"] == ITEM_DATATYPE:
            name = self.ProjectTree.GetItemText(selected)
            if self.CheckDataTypeIsUsedBeforeDeletion(name):
                self.Controler.ProjectRemoveDataType(name)
                tagname = ComputeDataTypeName(name)
                idx = self.IsOpened(tagname)
                if idx is not None:
                    idx.Close()
                self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, PROJECTTREE)

    def OnRenamePouMenu(self, event):
        selected = self.ProjectTree.GetSelection()
        if self.ProjectTree.GetItemData(selected)["type"] == ITEM_POU:
            wx.CallAfter(self.ProjectTree.EditLabel, selected)

    def OnRemovePouMenu(self, event):
        selected = self.ProjectTree.GetSelection()
        if self.ProjectTree.GetItemData(selected)["type"] == ITEM_POU:
            name = self.ProjectTree.GetItemText(selected)
            if self.CheckPouIsUsedBeforeDeletion(name):
                self.Controler.ProjectRemovePou(name)
                tagname = ComputePouName(name)
                idx = self.IsOpened(tagname)
                if idx is not None:
                    if USETAB:
                        self.TabsOpened.DeletePage(idx)
                    else:
                        idx.Close()
            self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL,
                          LIBRARYTREE)

    def OnRemoveTransitionMenu(self, event):
        selected = self.ProjectTree.GetSelection()
        item_infos = self.ProjectTree.GetItemData(selected)
        if item_infos["type"] == ITEM_TRANSITION:
            transition = self.ProjectTree.GetItemText(selected)
            pou_name = item_infos["tagname"].split("::")[1]
            self.Controler.ProjectRemovePouTransition(pou_name, transition)
            tagname = ComputePouTransitionName(pou_name, transition)
            idx = self.IsOpened(tagname)
            if idx is not None:
                if USETAB:
                    self.TabsOpened.DeletePage(idx)
                else:
                    idx.Close()
            self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE)

    def OnRemoveActionMenu(self, event):
        selected = self.ProjectTree.GetSelection()
        item_infos = self.ProjectTree.GetItemData(selected)
        if item_infos["type"] == ITEM_ACTION:
            action = self.ProjectTree.GetItemText(selected)
            pou_name = item_infos["tagname"].split("::")[1]
            self.Controler.ProjectRemovePouAction(pou_name, action)
            tagname = ComputePouActionName(pou_name, action)
            idx = self.IsOpened(tagname)
            if idx is not None:
                if USETAB:
                    self.TabsOpened.DeletePage(idx)
                else:
                    idx.Close()
            self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE)

    def OnRemoveConfigurationMenu(self, event):
        selected = self.ProjectTree.GetSelection()
        if self.ProjectTree.GetItemData(selected)["type"] == ITEM_CONFIGURATION:
            name = self.ProjectTree.GetItemText(selected)
            self.Controler.ProjectRemoveConfiguration(name)
            tagname = ComputeConfigurationName(name)
            idx = self.IsOpened(tagname)
            if idx is not None:
                if USETAB:
                    self.TabsOpened.DeletePage(idx)
                else:
                    idx.Close()
            self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL)

    def OnRemoveResourceMenu(self, event):
        selected = self.ProjectTree.GetSelection()
        item_infos = self.ProjectTree.GetItemData(selected)
        if item_infos["type"] == ITEM_RESOURCE:
            resource = self.ProjectTree.GetItemText(selected)
            config_name = item_infos["tagname"].split("::")[1]
            self.Controler.ProjectRemoveConfigurationResource(config_name, resource)
            tagname = ComputeConfigurationResourceName(config_name, selected)
            idx = self.IsOpened(tagname)
            if idx is not None:
                if USETAB:
                    self.TabsOpened.DeletePage(idx)
                else:
                    idx.Close()
            self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL)

    # -------------------------------------------------------------------------------
    #                        Highlights showing functions
    # -------------------------------------------------------------------------------

    def ShowHighlight(self, infos, start, end, highlight_type):
        self.SelectProjectTreeItem(infos[0])
        if infos[1] == "name":
            self.Highlights[infos[0]] = highlight_type
            self.RefreshProjectTree()
            self.ProjectTree.Unselect()
        else:
            self.EditProjectElement(GetElementType(infos[0]), infos[0])
            if USETAB:
                selected = self.TabsOpened.GetSelection()
                if selected != -1:
                    viewer = self.TabsOpened.GetPage(selected)
                    viewer.AddHighlight(infos[1:], start, end, highlight_type)
            else:
                selected = self.ActiveChild
                if selected and isinstance(selected.Children[0], Viewer):
                    viewer = selected
                    viewer.Children[0].AddHighlight(infos[1:], start, end, highlight_type)

    def ShowError(self, infos, start, end):
        self.ShowHighlight(infos, start, end, ERROR_HIGHLIGHT)

    def ShowSearchResult(self, infos, start, end):
        self.ShowHighlight(infos, start, end, SEARCH_RESULT_HIGHLIGHT)

    def ClearHighlights(self, highlight_type=None):
        if highlight_type is None:
            self.Highlights = {}
        else:
            self.Highlights = dict(
                [(name, highlight) for name, highlight in self.Highlights.items() if highlight != highlight_type])
        self.RefreshProjectTree()
        if USETAB:
            for i in range(self.TabsOpened.GetPageCount()):
                viewer = self.TabsOpened.GetPage(i)
                viewer.ClearHighlights(highlight_type)
        else:
            for i in self.ChildWindow():
                viewer = i
                if isinstance(i.Children[0], Viewer):
                    viewer.Children[0].ClearHighlights(highlight_type)

    def ClearErrors(self):
        self.ClearHighlights(ERROR_HIGHLIGHT)

    def ClearSearchResults(self):
        self.ClearHighlights(SEARCH_RESULT_HIGHLIGHT)

    def ShowMessage(self, message):
        message = wx.MessageDialog(self, message, _("Error"), wx.OK | wx.ICON_ERROR)
        message.ShowModal()
        message.Destroy()

    def RefreshTitle(self):
        name = self.title
        if self.CTR is not None:
            projectname = self.CTR.GetProjectName()
            if self.CTR.ProjectTestModified():
                projectname = "~%s~" % projectname
            self.SetTitle("%s - %s" % (name, projectname))
        else:
            self.SetTitle(name)


# -------------------------------------------------------------------------------
#                               Viewer Printout
# -------------------------------------------------------------------------------


def UPPER_DIV(x, y):
    return (x // y) + {True: 0, False: 1}[(x % y) == 0]


class GraphicPrintout(wx.Printout):
    def __init__(self, viewer, page_size, margins, preview=False):
        wx.Printout.__init__(self)
        self.Viewer = viewer
        self.PageSize = page_size
        if self.PageSize[0] == 0 or self.PageSize[1] == 0:
            self.PageSize = (1050, 1485)
        self.Preview = preview
        self.Margins = margins
        self.FontSize = 5
        self.TextMargin = 3

        maxx, maxy = viewer.GetMaxSize()
        self.PageGrid = (UPPER_DIV(maxx, self.PageSize[0]),
                         UPPER_DIV(maxy, self.PageSize[1]))

    def GetPageNumber(self):
        return self.PageGrid[0] * self.PageGrid[1]

    def HasPage(self, page):
        return page <= self.GetPageNumber()

    def GetPageInfo(self):
        page_number = self.GetPageNumber()
        return (1, page_number, 1, page_number)

    def OnBeginDocument(self, startPage, endPage):
        dc = self.GetDC()
        if not self.Preview and isinstance(dc, wx.PostScriptDC):
            dc.SetResolution(720)
        super(GraphicPrintout, self).OnBeginDocument(startPage, endPage)

    def OnPrintPage(self, page):
        dc = self.GetDC()
        dc.SetBackground(wx.WHITE_BRUSH)
        dc.Clear()
        dc.SetUserScale(1.0, 1.0)
        dc.SetDeviceOrigin(0, 0)
        dc.printing = not self.Preview

        # Get the size of the DC in pixels
        ppiPrinterX, ppiPrinterY = self.GetPPIPrinter()
        pw, ph = self.GetPageSizePixels()
        dw, dh = dc.GetSizeTuple()
        Xscale = (dw * ppiPrinterX) / (pw * 25.4)
        Yscale = (dh * ppiPrinterY) / (ph * 25.4)

        fontsize = self.FontSize * Yscale

        margin_left = self.Margins[0].x * Xscale
        margin_top = self.Margins[0].y * Yscale
        area_width = dw - self.Margins[1].x * Xscale - margin_left
        area_height = dh - self.Margins[1].y * Yscale - margin_top

        dc.SetPen(MiterPen(wx.BLACK))
        dc.SetBrush(wx.TRANSPARENT_BRUSH)
        dc.DrawRectangle(margin_left, margin_top, area_width, area_height)

        dc.SetFont(wx.Font(fontsize, wx.DEFAULT, wx.NORMAL, wx.NORMAL))
        dc.SetTextForeground(wx.BLACK)
        block_name = " - ".join(self.Viewer.GetTagName().split("::")[1:])
        _text_width, text_height = dc.GetTextExtent(block_name)
        dc.DrawText(block_name, margin_left, margin_top - text_height - self.TextMargin)
        dc.DrawText(_("Page: %d") % page, margin_left, margin_top + area_height + self.TextMargin)

        # Calculate the position on the DC for centering the graphic
        posX = area_width * ((page - 1) % self.PageGrid[0])
        posY = area_height * ((page - 1) // self.PageGrid[0])

        scaleX = area_width / self.PageSize[0]
        scaleY = area_height / self.PageSize[1]
        scale = min(scaleX, scaleY)

        # Set the scale and origin
        dc.SetDeviceOrigin(-posX + margin_left, -posY + margin_top)
        dc.SetClippingRegion(posX, posY, self.PageSize[0] * scale, self.PageSize[1] * scale)
        dc.SetUserScale(scale, scale)

        self.Viewer.DoDrawing(dc, True)

        return True
